单元测试Angular 2中的可观察量

时间:2016-01-05 09:18:12

标签: unit-testing jasmine angular observable

在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个对象一起使用。

6 个答案:

答案 0 :(得分:49)

Angular(版本2 +)的正确方法:

it('retrieves all the cars', async(inject( [CarService], ( carService ) => {
     carService.getCars().subscribe(result => expect(result.length).toBeGreaterThan(0)); 
}));

Async Observables vs Sync Observables

了解Observable可以是同步或异步非常重要。

在您的具体示例中,Observable是异步(它包装了一个http调用)。
因此,您必须使用async函数在特殊的异步测试区中执行其体内的代码。它拦截并跟踪其身体中创建的所有承诺,从而可以在完成异步操作时期望测试结果。

但是,如果您的Observable是同步,例如:

...
export class CarService{
    ...
    getCars():Observable<any>{
        return Observable.of(['car1', 'car2']);
    }
    ...

您不需要async功能,您的测试就会变得简单

it('retrieves all the cars', inject( [CarService], ( carService ) => {
     carService.getCars().subscribe(result => expect(result.length).toBeGreaterThan(0)); 
});

大理石

一般情况下测试Observable和Angular时要考虑的另一件事是marble testing

你的例子非常简单,但通常逻辑比调用http服务更复杂,测试这个逻辑变得很头疼。
弹珠使测试非常简短,全面(对于测试ngrx effects特别有用)。

如果您使用的是Jasmine,则可以Jest使用myRef.setvalue("Hello World"); // this will not work myRef.setValue("Hello World"); // this will work jasmine-marbles,但如果您更喜欢其他内容,则会jest-marbles ,这应该与任何测试框架兼容。

rxjs-marbles是用大理石复制和修复竞争条件的一个很好的例子。

Here

答案 1 :(得分:9)

最后,我以一个有效的例子结束。 Observable类有一个方法toPromise,它将Observable转换为Promise对象。正确的方法应该是:

it('retrieves all the cars', injectAsync( [CarService], ( carService ) => {
  return carService.getCars().toPromise().then( (result) => {         
     expect(result.length).toBeGreaterThan(0);
  } );       
}) );

但是,虽然上面的代码适用于任何Observable对象,但我仍然遇到从Http请求返回的Observable的问题,这可能是一个错误。以下是一个展示上述情况的掠夺者:http://plnkr.co/edit/ak2qZH685QzTN6RoK71H?p=preview

更新
从版本beta.14开始,它似乎与提供的解决方案一起正常工作。

答案 2 :(得分:2)

我推荐这种方法,我认为它更优雅:

f(&:itself)

您也可以使用Jasmine Matcher

提供自己的异步匹配器

答案 3 :(得分:1)

AsyncTestCompleter已弃用https://github.com/angular/angular/issues/5443injectAsync取代了它 https://github.com/angular/angular/issues/4715#issuecomment-149288405
injectAsync现已弃用
injectAsync不再被弃用https://github.com/angular/angular/pull/5721(另请参阅@ErdincGuzel的评论)

it('retrieves all the cars', injectAsync( [CarService], ( carService ) => {
     var c = PromiseWrapper.completer();
     carService.getCars().subscribe( result => {         
         expect(result.length).toBeGreaterThan(0);
         c.resolve();
     } ); 
     return c.promise;      
}) );

答案 4 :(得分:1)

我设法使其正常工作的方法是在期望之后进行订阅和致电。

it('should equal bar', (done: any) => {
 bar.getFoo().subscribe(v => {
  expect(v).toBe('foo');
  done();
 });
});

答案 5 :(得分:0)

https://angular.io/guide/testing当前显示了几种方法。这是一个:

it('#getObservableValue should return value from observable',
    (done: DoneFn) => {
       service.getObservableValue().subscribe(value => {
       expect(value).toBe('observable value');
       done();
    });
});