Angular 2 RC4: Unit test компонент, у которого есть инъекция зависимостей, служба, которая имеет свою собственную

Как команда Angular постоянно обновляет/стирает вещи в версиях Angular 2 RC, я столкнулся с этой проблемой.

У меня есть компонент, который имеет Injection Dependency (DI), который на самом деле является сервисом (UserService в этом случае). У этого UserService, конечно же, есть собственные DI. После обновления до последнего RC4 Angular 2 я понял, что больше не могу создавать подобные тесты.

Так как документы не упоминают что-то относительное здесь, мой код (упрощенный для этого вопроса).

Мой компонент:

import { Component } from '@angular/core';
import { MdButton } from '@angular2-material/button';
import {
  MdIcon,
  MdIconRegistry
} from '@angular2-material/icon';
import { UserService } from '../../services/index';

@Component({
  moduleId: module.id,
  selector: 'logout-button',
  templateUrl: 'logout-button.component.html',
  styleUrls: ['logout-button.component.css'],
  providers: [MdIconRegistry, UserService],
  directives: [MdButton, MdIcon]
})
export class LogoutButtonComponent {

  constructor(public userService: UserService) {}

  /**
   * Call UserService and logout() method
   */
  logout() {
    this.userService.logout();
  }

}

Компонент DI, UserService, который, как вы можете видеть, имеет некоторые DI (Router, AuthHttp и Http):

import { Injectable } from '@angular/core';
import {
  Http,
  Headers
} from '@angular/http';
import {
  AuthHttp,
  JwtHelper
} from 'angular2-jwt';
import { Router } from '@angular/router';
import { UMS } from '../common/index';

@Injectable()
export class UserService {

  constructor(
    private router: Router,
    private authHttp: AuthHttp,
    private http: Http) {

      this.router = router;
      this.authHttp = authHttp;
      this.http = http;
    }

    /**
     * Logs out user
     */
    public logout() {
      this.authHttp.get(UMS.url + UMS.apis.logout)
      .subscribe(
        data => this.logoutSuccess(),
        err => this.logoutSuccess()
      );
    }

}

И вот тест для компонента:

import { By }           from '@angular/platform-browser';
import { DebugElement } from '@angular/core';

import {
  beforeEach,
  beforeEachProviders,
  describe,
  expect,
  it,
  inject,
  fakeAsync,
  TestComponentBuilder
} from '@angular/core/testing';

import { AuthHttp } from 'angular2-jwt';
import { Router } from '@angular/router';
import { Http } from '@angular/http';
import { LogoutButtonComponent } from './logout-button.component';
import { UserService } from '../../services/index';

describe('Component: LogoutButtonComponent', () => {



  beforeEachProviders(() => [
    LogoutButtonComponent,
    UserService
  ]);

  it('should inject UserService', inject([LogoutButtonComponent],
    (component: LogoutButtonComponent) => {
      expect(component).toBeTruthy();
  }));

});

Не беспокойтесь о (сейчас).

Как вы можете видеть, я добавляю связанных поставщиков на beforeEachProviders.

В этом случае я получаю сообщение об ошибке при выполнении тестов:

Ошибка: нет провайдера для маршрутизатора! (LogoutButtonComponent → UserService → Маршрутизатор)

Что ожидается, скажем.

Итак, чтобы не получить эти ошибки, я также добавляю служебные DI в провайдеры:

  beforeEachProviders(() => [
    LogoutButtonComponent,
    Router,
    AuthHttp,
    Http,
    UserService
  ]);

Но теперь я получаю эту ошибку:

Ошибка: не удается разрешить все параметры для "Router" (?,?,?,?,?,?,?). Убедитесь, что все параметры украшены Inject или имеют действительные аннотации типов и что "Router" украшен Injectable.

Я действительно пытаюсь понять, что происходит, поэтому я нашел некоторые связанные ответы здесь, но ВСЕ устарели и покрывают старые router-deprecated или angular2/router, но оба они устарели и не покрывают этот случай.

Хотелось бы немного помочь в этом и, возможно, в некоторых ресурсах, поскольку я не могу найти ничего связанного с последней версией Router: "@angular/router": "3.0.0-beta.2" и RC4.

Спасибо

ОБНОВЛЕНИЕ!

Мне удается обойти две ошибки выше, и теперь я могу получить доступ к компоненту. Вот код описания:

describe('Component: LogoutButtonComponent', () => {

  let component: LogoutButtonComponent;
  let router: any = Router;
  let authHttp: any = AuthHttp;
  let http: any = Http;
  let service: any = new UserService(router, authHttp, http);

  beforeEachProviders(() => [
    LogoutButtonComponent
  ]);

  beforeEach(() => {
    component = new LogoutButtonComponent(service);
  });

  it('should inject UserService', () => {
    expect(component.userService).toBeTruthy();
  });

  it('should logout user', () => {
    localStorage.setItem('token', 'FOO');
    component.logout();
    expect(localStorage.getItem('token')).toBeUndefined();
  });

});

Но кажется, что даже если услуга DI введена и доступна, DI-служба не является. Итак, теперь я получаю эту ошибку:

TypeError: this.authHttp.get не является функцией

Любые идеи?

Ответ 1

Похоже, что у вас возникла проблема с циклами зависимостей, потому что вашему UserSerivce также нужно вводить AuthHttp, Http и т.д.... это действительно будет беспокоить один раз, если вам нужно проверить свой компонент. Мой способ - просто создать mock UserSerivce и вернуть ожидаемое издеваемое значение с помощью метода UserService.logout(), потому что вам не нужно знать, что действительно произошло в UserService, все, что вам нужно, это просто возвращаемое значение:

let MockUserService = {
  logout() {
    // return some value you need 
  }
}

Затем в наборе тестов:

import { provide } from '@angular/core'

beforeEachProviders(() => [
  provide(UserService, {useClass: MockUserService})
])

... detail test code here

Надеюсь, это сработает для вас. И вот сообщение, которое мне очень помогает: https://developers.livechatinc.com/blog/testing-angular-2-apps-dependency-injection-and-components/

Ответ 2

В RC4 для использования http в тесте необходимы некоторые обходные пути. См. Также эту проблему (следует исправить в RC5):

https://github.com/angular/angular/issues/9294

Добавление этого в мой unit-tests.html исправлено для меня:

System.import('@angular/platform-browser/src/browser/browser_adapter').then(function(browser_adapter) {
    browser_adapter.BrowserDomAdapter.makeCurrent();
})