我需要为以下DataService
编写单元测试,
@Injectable()
export class DataService {
constructor(private config: ConfigService, private http: HttpClient) {
}
.....
someMethod(){
let apiUrl = this.config.get('api').url; // LINE 1
}
}
ConfigService
被注入DataService
,其load
函数从json文件获取配置。初始化应用程序时将调用此load
函数。
export function configServiceFactory(config: ConfigService) {
return () => config.load();
}
...
providers: [
ConfigService,
{
provide: APP_INITIALIZER,
useFactory: configServiceFactory,
deps: [ConfigService],
multi: true
}
]
以下是data-service.spect.ts
文件的一部分,
...
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule
],
providers: [DataService, ConfigService]
});
mock = TestBed.get(HttpTestingController);
service = TestBed.get(DataService);
});
....
所以当我运行测试时,在LINE 1
我得到this.config.get('api')
未定义。我可以理解,这是因为ConfigService
没有从JSON加载数据。那么现在我如何使注入的服务也在单元测试期间进行异步调用?
答案 0 :(得分:2)
编写单元测试时,您可能希望模拟您拥有的每个依赖项。您已通过导入HttpClient
为HttpClientTestingModule
执行此操作,因此您需要对ConfigService
执行相同操作。
有两种方法可以做到这一点。
1-假服务(存根)
export class ConfigServiceStub {
get(input: string) {
// return static value instead of calling an API
}
}
...
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule
],
providers: [DataService,
{provide: ConfigService, useClass: ConfigServiceStub}
]
});
mock = TestBed.get(HttpTestingController);
service = TestBed.get(DataService);
});
通过这种方式,您的DataService
将不会调用真实的ConfigService
,而是会调用get
的{{1}}方法。使用ConfigServiceStub
时,您无需担心其他依赖项Stub
。您只需实现要覆盖的方法。
<强> 2-间谍强>
您可以在不想调用的方法上创建间谍。
ConfigService
即使在上面的示例中不会调用it('run some test', inject([DataService], (service: DataService) => {
spyOn(service.config, 'get').and.returnValue('someString');
// run your tests here
});
方法,Angular仍然需要创建ConfigService.get
的实例,在某些示例中可能很难这样做,或者可能导致创建太多其他服务用于简单测试。
我选择选项1
有关间谍的更多信息,请查看here