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
オブジェクトで動作するため、私の知る限りでは役に立ちません。
最後に、実際の例で終わります。 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リクエストから返されるObservable
sにはまだバグがあります。上記のケースを示すプランカーは次のとおりです。 http://plnkr.co/edit/ak2qZH685QzTN6RoK71H?p=preview
更新:
バージョンbeta.14の時点では、提供されたソリューションで適切に動作するようです。
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 があります。これは、任意のテストフレームワークと互換性があります。
ここ は、ビー玉で競合状態を再現および修正するための素晴らしい例です。
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(); }); });
AsyncTestCompleter
は非推奨です https://github.com/angular/angular/issues/544 。 injectAsync
はそれを置き換えました 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;
}) );