在Angular 2中单元测试返回Observable结果的服务的正确方法是什么?假设我们在CarService服务类中有一个getCars方法:
... export class CarService{ ... getCars():Observable<any>{ return this.http.get("http://someurl/cars").map( res => res.json() ); } ... }
如果我尝试按以下方式编写测试,我会收到警告:“SPEC没有预期”:
it('retrieves all the cars',inject( [CarService],( carService ) => { carService.getCars().subscribe( result => { expect(result.length).toBeGreaterThan(0); } ); }) );
使用injectAsync没有帮助,因为就我所见,它与Promise对象一起使用。
Angular的正确方法(第2版):
it('retrieves all the cars',async(inject( [CarService],( carService ) => { carService.getCars().subscribe(result => expect(result.length).toBeGreaterThan(0)); }));
Async Observables与Sync Observables
重要的是要理解Observable可以是同步的也可以是异步的。
在您的特定示例中,Observable是异步的(它包装了一个http调用)。
因此,您必须使用async
函数在特殊的异步测试区域中执行其体内的代码。它拦截并跟踪其主体中创建的所有承诺,从而可以在完成异步操作时期望测试结果。
但是,如果您的Observable是同步的,例如:
... export class CarService{ ... getCars():Observable<any>{ return Observable.of(['car1','car2']); } ...
你不需要异步功能,你的测试就会变得简单
it('retrieves all the cars',( carService ) => { carService.getCars().subscribe(result => expect(result.length).toBeGreaterThan(0)); });
大理石
另一方面,一般情况下测试Observable和Angular时需要考虑的是marble testing。
你的例子很简单,但通常逻辑比调用http服务更复杂,测试这个逻辑变得很头疼。
弹珠使测试非常简短,全面(对于测试ngrx effects特别有用)。
如果您使用Jasmine,您可以使用jasmine-marbles,对于Jest,有jest-marbles,但如果您更喜欢其他内容,则有rxjs-marbles,它应该与任何测试框架兼容。
Here是用弹珠复制和修复比赛条件的一个很好的例子。