typescript - Angular 2 RC4: Enhetstesta komponent som har beroendeinjektion av en tjänst som har sin egen
Eftersom Angular-teamet ständigt uppgraderar/avvecklar saker i Angular 2 RC-versioner stötte jag på detta problem.
Jag har en komponent som har Dependency Injection (DI), vilket faktiskt är en tjänst (UserService i det här fallet). Denna UserService har naturligtvis några DI:er av sina egna. Efter att ha uppdaterat till den senaste RC4 av Angular 2 insåg jag att jag inte kan skapa liknande tester längre.
Så eftersom dokumentationen inte nämner något relevant här är här min kod (förenklad för den här frågan).
Min komponent:
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();
}
}
Komponentens DI, UserService som du kan se har några DIs (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()
);
}
}
Och här är testet för komponenten:
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();
}));
});
Oroa dig inte för det än så länge.
Som du kan se lägger jag till de relaterade leverantörerna på beforeEachProviders.
I det här fallet får jag ett fel när jag kör testerna:
Fel: Ingen leverantör för Router! (LogoutButtonComponent -> UserService -> Router)
Vilket förväntas, låt oss säga.
Så för att inte få de där felen lägger jag till tjänstens DI:er i providers också:
beforeEachProviders(() => [
LogoutButtonComponent,
Router,
AuthHttp,
Http,
UserService
]);
Men nu får jag detta felmeddelande:
Fel: Kan inte lösa alla parametrar för 'Router'(?, ?, ?, ?, ?, ?, ?). Se till att alla parametrar är dekorerade med Inject eller har giltiga typannoteringar och att 'Router' är dekorerad med Injectable.
Jag försöker verkligen lista ut vad som händer, så jag hittade några relaterade svar här men ALLA är föråldrade och täcker den gamla router-deprecated eller angular2/router men båda är föråldrade och täcker inte detta fall.
Skulle uppskatta lite hjälp med detta och kanske några resurser eftersom jag inte kan hitta något relaterat till den senaste versionen av Router: "@angular/router": "3.0.0-beta.2", och RC4.
Tack
UPPDATERING!
Jag lyckas kringgå de två felen ovan och nu kan jag få åtkomst till komponenten. Här är beskrivningskoden:
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();
});
});
Men det verkar som att även om DI-tjänsten är injicerad och åtkomlig så är DI:erna för tjänsten det inte. Så nu får jag detta fel:
TypeError: this.authHttp.get är inte en funktion
Några idéer?
Svar #1
Det verkar som att du upplevde ett problem med en beroendecykel, eftersom din UserService också behöver injicera AuthHttp, Http, etc... det blir verkligen störande om du behöver testa din komponent. Mitt sätt är bara att skapa en mockad UserService och returnera det förväntade mockade värdet genom UserService.logout()-metoden, eftersom du inte behöver veta vad som verkligen händer i UserService, allt du behöver är bara ett returvärde:
let MockUserService = {
logout() {
// return some value you need
}
}
Sedan, i testsuiten:
import { provide } from '@angular/core'
beforeEachProviders(() => [
provide(UserService, {useClass: MockUserService})
])
... detail test code here
Jag hoppas att detta fungerar för dig. Och här är ett inlägg som hjälper mig mycket: https://developers.livechatinc.com/blog/testing-angular-2-apps-dependency-injection-and-components/
Svar #2
Med RC4 behövs vissa lösningar för att använda http i ett test. Se även detta problem (bör åtgärdas i RC5):
https://github.com/angular/angular/issues/9294
Att lägga till detta i min unit-tests.html fixade det för mig:
System.import('@angular/platform-browser/src/browser/browser_adapter').then(function(browser_adapter) {
browser_adapter.BrowserDomAdapter.makeCurrent();
})
