我正在尝试为我的服务中使用的组件编写单元测试。 组件和服务工作正常。
组件:
import {Component} from '@angular/core';
import {PonyService} from '../../services';
import {Pony} from "../../models/pony.model";
@Component({
selector: 'el-ponies',
templateUrl: 'ponies.component.html',
providers: [PonyService]
})
export class PoniesComponent {
ponies: Array<Pony>;
constructor(private ponyService: PonyService) {
this.ponies = this.ponyService.getPonies(2);
}
refreshPonies() {
this.ponies = this.ponyService.getPonies(3);
}
}
服务:
import {Injectable} from "@angular/core";
import {Http} from "@angular/http";
import {Pony} from "../../models/pony.model";
@Injectable()
export class PonyService {
constructor(private http: Http) {}
getPonies(count: number): Array<Pony> {
let toReturn: Array<Pony> = [];
this.http.get('http://localhost:8080/js-backend/ponies')
.subscribe(response => {
response.json().forEach((tmp: Pony)=> { toReturn.push(tmp); });
if (count && count % 2 === 0) { toReturn.splice(0, count); }
else { toReturn.splice(count); }
});
return toReturn;
}}
组件单元测试:
import {TestBed} from "@angular/core/testing";
import {PoniesComponent} from "./ponies.component";
import {PonyComponent} from "../pony/pony.component";
import {PonyService} from "../../services";
import {Pony} from "../../models/pony.model";
describe('Ponies component test', () => {
let poniesComponent: PoniesComponent;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [PoniesComponent, PonyComponent],
providers: [{provide: PonyService, useClass: MockPonyService}]
});
poniesComponent = TestBed.createComponent(PoniesComponent).componentInstance;
});
it('should instantiate component', () => {
expect(poniesComponent instanceof PoniesComponent).toBe(true, 'should create PoniesComponent');
});
});
class MockPonyService {
getPonies(count: number): Array<Pony> {
let toReturn: Array<Pony> = [];
if (count === 2) {
toReturn.push(new Pony('Rainbow Dash', 'green'));
toReturn.push(new Pony('Pinkie Pie', 'orange'));
}
if (count === 3) {
toReturn.push(new Pony('Fluttershy', 'blue'));
toReturn.push(new Pony('Rarity', 'purple'));
toReturn.push(new Pony('Applejack', 'yellow'));
}
return toReturn;
};
}
package.json的一部分:
{
...
"dependencies": {
"@angular/core": "2.0.0",
"@angular/http": "2.0.0",
...
},
"devDependencies": {
"jasmine-core": "2.4.1",
"karma": "1.2.0",
"karma-jasmine": "1.0.2",
"karma-phantomjs-launcher": "1.0.2",
"phantomjs-prebuilt": "2.1.7",
...
}
}
当我执行&#39; karma start&#39;我收到此错误
错误:./PoniesComponent类中的错误PoniesComponent_Host - 内联模板:0:0引起:没有Http的提供者!在config / karma-test-shim.js
中
看起来业力使用PonyService
而不是将其模仿为MockPonyService
,尽管有这一行:providers: [{provide: PonyService, useClass: MockPonyService}]
。
问题:我应该如何嘲笑服务?
答案 0 :(得分:20)
这是因为这个
@Component({
providers: [PonyService] <======
})
这使得服务的范围限定为组件,这意味着Angular将为每个组件创建它,并且还意味着它取代在模块级别配置的任何全局提供者。这包括您在测试台中配置的模拟提供程序。
为了解决这个问题,Angular提供了TestBed.overrideComponent
方法,它允许我们覆盖@Component.providers
和@Component.template
之类的内容。
TestBed.configureTestingModule({
declarations: [PoniesComponent, PonyComponent]
})
.overrideComponent(PoniesComponent, {
set: {
providers: [
{provide: PonyService, useClass: MockPonyService}
]
}
});
答案 1 :(得分:1)
另一个有效的方法是使用标记并依赖于Intefaces而不是基类或具体类,这些恐龙喜欢我做(DIP,DI和其他SOLID Blablahs)。并允许您的组件注入其依赖项,而不是自己在您自己的组件中提供它。
您的组件没有任何提供程序,它会在angular的 magic 依赖项注入期间在其构造函数中接收对象作为接口。请参阅构造函数中使用的@inject,并将提供者中的“提供”值视为文本而不是类。
因此,您的组件将更改为:
constructor(@Inject('PonyServiceInterface') private ponyService: IPonyService) {
this.ponies = this.ponyService.getPonies(2); }
在@Component部分中,您将删除提供程序并将其添加到父组件,例如“app.component.ts”。在那里你会添加一个令牌:
providers: [{provide: 'PonyServiceInterface', useClass: PonyService}]
您的单元测试组件(类似于app.component.ts)将具有: 提供者:[{provide:'PonyServiceInterface',useClass:MockPonyService}]
因此,您的组件不关心服务的作用,它只使用通过父组件(app.component.ts或您的单元测试组件)注入的接口。
仅供参考:@inject方法的使用并不是很广泛,在某些时候,由于底层的javascript工作方式,看起来像有角度的人更喜欢基类到接口。