2017-04-18 30 views
3

私は、Angular 2プロジェクトでジャスミンを使って統合テストを作成しようとしています。サービスとデータを模擬して適切な単体テストを実行することができますが、今ではコンポーネントでサービスを使用して実際にバックエンドAPIに接続できることを確認したいと考えています。これまでのところ、コンポーネントとサービスの両方のメソッドがスパイからトリガーされていることを確認できますが、コンポーネント内の.subscribe(....)メソッドはテストが終了するまで完了しません。次のように私の仕様のための私のコードは次のとおりです。Angular 2モックなしで観測可能なコンポーネントをテストする

import { async, ComponentFixture, TestBed, inject, fakeAsync } from '@angular/core/testing'; 
import { DebugElement } from "@angular/core"; 
import { By } from "@angular/platform-browser"; 

import { ReviewComponent } from './review.component'; 
import { FormsModule } from '@angular/forms'; 
import { UrlService } from 'app/shared/services/url.service'; 
import { HttpModule, Http } from '@angular/http'; 
import { ReviewService } from "./review.service"; 

describe('CommunicationLogComponent',() => { 
    let component: ReviewComponent; 
    let fixture: ComponentFixture<ReviewComponent>; 
    let serviceSpy: jasmine.Spy; 
    let componentSpy: jasmine.Spy; 
    let service: ReviewService; 

    beforeEach(() => { 
    TestBed.configureTestingModule({ 
     imports: [FormsModule, HttpModule], 
     providers: [UrlService], 
     declarations: [ReviewComponent] 
    }) 
     .compileComponents(); 
    }); 

    beforeEach(() => { 
    fixture = TestBed.createComponent(ReviewComponent); 
    component = fixture.componentInstance; 
    component.processId = 6; 
    service = fixture.debugElement.injector.get(ReviewService); 
    componentSpy = spyOn(component, "ngOnInit").and.callThrough(); 
    serviceSpy = spyOn(service, 'getReviewByProcess').and.callThrough(); 
    }); 

    it('should create component',() => { 
    expect(component).toBeTruthy(); 
    }); 
    it('should not have called ngOnInit() yet',() => { 
    expect(componentSpy).not.toHaveBeenCalled(); 
    }); 

    it('should make a call to ngOnInit() and by extension getReviewByProcess',() => { 
    fixture.detectChanges(); 
    expect(componentSpy).toHaveBeenCalled(); 
    expect(serviceSpy).toHaveBeenCalled(); 
    }); 
    //ISSUES HERE 
    it('should show review after the getReviewByProcess resolves', async(() => { 
    fixture.detectChanges(); 
    fixture.whenStable().then(() => { 
     expect(componentSpy).toHaveBeenCalled(); 
     fixture.detectChanges(); 
     expect(component.Reviews[0]).not.toBe(undefined); 
    }); 
    }), 5000);   
}); 

アップ全ての試験は「HERE //問題」でマークされた1が正常に動作し、合格するまで。

私はasync()を使用して、テストを終了する前にフィクスチャが安定するのを待っています。また、componentSpyを介して、ngOnInitが呼び出されたことが確認されます(バックエンドに接続してレビューに登録します)。

マイコンポーネントのコードは次のようになります。

import { Component, EventEmitter, Input, OnInit } from '@angular/core'; 

//Service 
import { ReviewService } from "app/process/monitoring/shared/components/review/review.service"; 

//Classes 
import { Review } from "app/process/monitoring/shared/components/review/review"; 

@Component({ 
    providers: [ 
    ReviewService 
    ], 
    selector: 'monitoring-review', 
    templateUrl: './review.component.html', 
    styleUrls: ['./review.component.css'] 
}) 
export class ReviewComponent implements OnInit { 

    public Review: Review = new Review(); 
    public Reviews: Review[] = []; 

    @Input() public processId: number; 

    constructor(
    private ReviewService: ReviewService 
) { } 


    ngOnInit(): void { 
    this.ReviewService.getReviewByProcess(this.processId, true).first().subscribe(
     Reviews => { 
     if (Reviews) { 
      this.Reviews = Reviews //doesn't trigger until test is finished 
     } 
     }, 
     error => console.log(error) 
    ); 
    } 
} 

そして最後に、私のReviewServiceは次のようになりますカルマのデバッガを使用して

import { Injectable } from '@angular/core'; 
import { Http, Response } from '@angular/http'; 
import { Headers, RequestOptions, RequestMethod, URLSearchParams } from '@angular/http'; 
import { Observable } from 'rxjs/Observable'; 

import 'rxjs/add/observable/throw'; 
import 'rxjs/add/operator/catch'; 
import 'rxjs/add/operator/map'; 

// App-wide Services 
import { UrlService } from 'app/shared/services/url.service'; 

// Classes 
import { Review } from "app/process/monitoring/shared/components/review/review"; 


@Injectable() 
export class ReviewService { 

    private devUrl: string; 
    private jsonHeaders: Headers = new Headers({ 'Content-Type': 'application/json' }); 
    private prodUrl: string; 
    private reviewPath: string = 'monitoring/program/'; 

    constructor(
    private urlService: UrlService, 
    private http: Http 
) { 
    this.devUrl = this.urlService.getUrl('dev').url; 
    this.prodUrl = this.urlService.getUrl('prod').url; 
    } 

    getReviewByProcess(processId: number, isProd: boolean = false): Observable<Review[]> { 
    let targetUrl: string = (isProd ? this.prodUrl : this.devUrl) + this.reviewPath + "/" + processId; 

    return this.http.get(targetUrl, { withCredentials: true }) 
     .map((res: Response) => res.json()) 
     .catch((error: any) => Observable.throw(error.json().error || 'Server error') 
    ); 
    }; 
} 

、私は私のテストはどのそして、ngOnInitを呼び出すことを確認することができますこのサービスへの呼び出しは、テストが完了するまで、購読しているコンポーネントの内部(「//はトリガーしません」とマークされています)に当たることはありません。

要約:「ライブ」バックエンドを使用してデータを収集するサービスと統合するコンポーネントをテストできるテストを作成したいと考えています。それはサービスであり、私のコンポーネントは何も嘲笑しません。私のメソッドに到達したが、私のReviewComponentのngOnInit()メソッドで見つかったサブスクライブ(...)は、テストが完了するか失敗するまで終了しない。

答えて

0

モックデータを返すにはサービスが必要です。次のようなものがあります:

+0

モックデータは返信したくないのですが、私のウェブAPIから正当なデータを返すことはできません。これはジャスミンがサポートできないものですか?分度器を使って解決策を探すべきですか? – LandSharks

+0

これは単体テストであり、スコープで特定のコンポーネント/機能専用であり、環境をモックしてアーカイブする必要があります。 httpをモックする場合は、MockBackendを使用してhttpサービスレベルを模擬することができます。 Web APIを使用してエンドツーエンドテスト用のテスト用の分度器を呼び出す必要があるが、角度2のユニットテストの優れた機能があれば、e2eの必要性はそれほどありません。 –

+0

Jasmineは統合テストをサポートしていませんか?私は角度2のためのメインのドキュメンタリーサイトに気づいた:https://angular.io/docs/ts/latest/guide/testing.html#!#component-with-async-service 彼らは私が正確なことをやっていること私は気づいたhttp接続を望んでいません。彼らのオブジェクトには静的なデータがありました。 – LandSharks

2

非同期メソッドが待機するためには、observableを返し、サブスクライブする必要があります。例えば、次のような仮想テストが機能します。

it('', async(() => { 
    observable().subscribe(
    (review) => { 
     expect(component.Reviews[0]).not.toBe(undefined) 
    } 
) 
})); 

問題は、テストで実行する非同期呼び出しを待っていないことです。角度HTTPサービスを少し変更することもできます。

import { Http, HttpModule, BaseRequestOptions, XHRBackend, HttpModule, ConnectionBackend } from '@angular/http'; 
const httpSubject = new Subject() 
const http$ = httpSubject.asObservable() 
const httpFactory = (backend, options) => { 
    const newHttp = new Http(backend, options); 
    const helperHttp = new Http(backend, options); 
    newHttp.get = function(strng, options) { 
    return helperHttp.get(strng, options).do((response) => { 
     setTimeout(() => { 
     httpSubject.next({}); 
     }, 300); 
    }); 
    }; 
    return newHttp; 
} 

TestBed.configureTestingModule({ 
    imports: [HttpModule, FormModule], 
    providers: [ 
    UrlService, 
    ConnectionBackend, 
    BaseRequestOptions 
    { 
     provide: Http, 
     useFactory: httpFactory, 
     deps: [ XHRBackend, BaseRequestOptions] 
    } 
    ], 
    declarations: [ReviewComponent] 
}) 

it('should show review after the getReviewByProcess resolves', 
    async(() => { 
    http$.subscribe(() => { 
     expect(componentSpy).toHaveBeenCalled(); 
     expect(component.Reviews[0]).not.toBe(undefined); 
    }) 
    fixture.detectChanges(); 
})); 
+0

例として設定したコードでは、適切なURL、オプションなどを配置してもコールスタックエラーが発生しました。 – LandSharks

+0

@LandSharks申し訳ありませんが、私はそれを更新しており、ローカルで私のために働いています。試してみてください。まだ問題がある場合は、作成したテストバージョンをお送りします。 –

+0

@LandSharksあなたがそれを試してみたり、別の解決策を見つけた場合は、コメントしてください。それは知っていると良いでしょう。 –

関連する問題