web-dev-qa-db-ja.com

Angular 2のオブザーバブルの単体テスト

Angular 2のObservable結果を返すサービスを単体テストする正しい方法は何ですか? CarServiceサービスクラスにgetCarsメソッドがあるとします。

...
export class CarService{
    ...
    getCars():Observable<any>{
        return this.http.get("http://someurl/cars").map( res => res.json() );
    }
    ...
}

次の方法でテストを記述しようとすると、「SPEC HAS NO Expectations」という警告が表示されます。

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

InjectAsyncの使用は、Promiseオブジェクトで動作するため、私の知る限りでは役に立ちません。

44
Erdinc Guzel

最後に、実際の例で終わります。 Observableクラスには、ObservableをPromiseオブジェクトに変換するtoPromiseメソッドがあります。正しい方法は次のとおりです。

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

しかし、上記のコードは任意のObservableオブジェクトで機能しますが、Httpリクエストから返されるObservablesにはまだバグがあります。上記のケースを示すプランカーは次のとおりです。 http://plnkr.co/edit/ak2qZH685QzTN6RoK71H?p=preview

更新:
バージョンbeta.14の時点では、提供されたソリューションで適切に動作するようです。

9
Erdinc Guzel

Angular(ver。2+)の正しい方法:

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

非同期オブザーバブルと同期オブザーバブル

Observablesは、同期または非同期のいずれかにできることを理解することが重要です。

具体的な例では、Observableはasynchronousです(http呼び出しをラップします)。
したがって、特別なasync test zoneで本体内のコードを実行する async 関数を使用する必要があります。本体で作成されたすべてのプロミスをインターセプトして追跡し、非同期アクションの完了時にテスト結果を期待できるようにします。

ただし、Observableがsynchronousの場合、たとえば:

...
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をテストする際に考慮すべきもう1つのことは、 marble testing です。

あなたの例は非常に単純ですが、通常、ロジックは単にhttpサービスを呼び出してこのロジックをテストするよりも複雑です。
大理石は、テストを非常に短く、シンプルで包括的なものにします(テスト ngrx効果 )には特に役立ちます)。

Jasmineを使用している場合は、 jasmine-marbles を使用できます。Jestには jest-marbles がありますが、それ以外の場合は、 rxjs-marbles があります。これは、任意のテストフレームワークと互換性があります。

ここ は、ビー玉で競合状態を再現および修正するための素晴らしい例です。


テストの公式ガイド

54
JeB

https://angular.io/guide/testing は現在いくつかの方法を示しています。以下がその1つです。

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

AsyncTestCompleterは非推奨です https://github.com/angular/angular/issues/544injectAsyncはそれを置き換えました 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;      
}) );
1