2017-05-19 8 views
0

このログイン機能、具体的にはhttpのポスト部分をユニットテストするにはどうすればよいですか?私が作ったhttpモックは、コードのif ... elseセクションに入るために正しくコード化されていません。私はTestBedを使いたくない。 TestBedが遅すぎます。以下はTestBedを使わずにhttp postをAngularでモックアウトするにはどうすればよいですか?

login(username: string, password: string): Observable<boolean> { 

    const headers = new Headers(); 
    headers.append('Content-Type', 'application/json'); 
    headers.append('accept', 'application/json'); 
    let options = new RequestOptions({ headers: headers }); 

    return this.http.post('https://blah/api/login', 
     JSON.stringify({ username: username, password: password }), options) 
     .map((response: Response) => { 
      const token = response.json() && response.json().access_token; 
      if (token) { 
       this.token = token; 
       localStorage.setItem('currentUser', JSON.stringify({ username: username, token: token })); 
       return true; 
      } else { 
       return false; 
      } 
     }).catch(this._serverError); 
} 

private _serverError(err: any) { 
    return Observable.throw(err || 'backend server error'); 
} 

ジャスミンテストImがしようとしている:私は、この行を支援する必要があります。

spyOn(mockHttp,'post').and.returnValue(Observable.of(response)); 

私のreturnValueは、ログイン機能の 'if ... else'コードの中に入るべきですか?

describe('AuthenticationService',() => { 
     let service: AuthenticationService; 
     let mockHttp = null; 

     beforeEach(() => { 
     mockHttp = {}; 
     mockHttp.post = function(){}; 
     service = new AuthenticationService(mockHttp); 
    }); 

    it(`should set access token in local storage for successful login`,() => { 
     const access_token = 'blah83balc380'; 
     const responseOptions = new ResponseOptions(); 
     responseOptions.status = 200; 
     responseOptions.body = {access_token:access_token}; 
     const username = 'test'; 
     const currentUserExpected = JSON.stringify({ username: username, token: access_token }); 
     var response = new Response(responseOptions); 
     spyOn(mockHttp,'post').and.returnValue(Observable.of(response)); 
     service.login(username, 'test'); 
     var currentUser = localStorage.getItem('currentUser'); 
     expect(currentUserExpected).toEqual(currentUser); 
    }); 
    }); 
+1

あなたはそのようなことをしたいのですか? 'json()'関数などをモックする必要があります。ちょっと面倒です。あなたがそれを扱うことができないので、これはTestBedでのテストが優れているという証拠と考えることができます。 Btw、コードの書式設定が間違っている、質問は読めません。 – estus

+0

@estus TestBedテストは遅いです。 TestBedを使用する100回のテストを実行するのにどれくらい時間がかかりますか?長すぎる。だから絶対に必要でない限りTestBedを使いたくないのです。 TestBedによるテストは優れていません。あなたのコメントは役に立たず、役に立たない。 – user372225

+0

あなたがそれを感謝しなければ、これはコメントを役に立たないものにしません。彼らが完全なカバレッジを提供しない場合、効率的な100回のテストはどれくらいですか? TestBedでDIを正しくテストできます。あなたはそれなしではできません。あなたが質問の中でパフォーマンスに問題があるという言葉さえありません。待ってください、言及がありますが、フォーマットが正しくないので見えません。 – estus

答えて

0

あなたは

it('should do something',inject([YourService,XHRBackend],(service:YourService,mockBackend)=>{ 
mockBackend.connections.subscribe((connection)=>{ 
    connection.mockRespond(new Response({body:JSON.stringify('your mock response')}); 

のようなあなたのテストにサービスを注入することができます...

これはそのモックデータを返すようにあなたのポストの呼び出しが発生します。あなたが実際には最初のテストベッドをやってこれは私が使用して終了答えです

{provide: XHRBackend, useClass: MockBackend} 
+0

私は数日前にこの方法を試みました。私はTestBedを使わずに働くための '提供'を得ることができませんでした。しかし、アイデアをありがとう! – user372225

0

提供せずにXHRBackendを注入することができる場合、どのよう私も一定ではないよことです。私は以下のコードのようなテストベッドを使用していることができるときに遅かった方法でテストベッドを使用していました:

import { TestBed, inject} from '@angular/core/testing'; 
import { HttpModule, XHRBackend, Response, ResponseOptions } from '@angular/http'; 
import { AuthenticationService } from '../_services/authentication.service'; 
import { MockBackend } from '@angular/http/testing'; 

describe('AuthenticationService',() => { 
    let mockbackend, service; 

    beforeEach(() => { 
    localStorage.clear(); 
    TestBed.configureTestingModule({ 
     imports: [ HttpModule ], 
     providers: [ 
     AuthenticationService, 
     { provide: XHRBackend, useClass: MockBackend } 
     ] 
    }); 
    }); 

    beforeEach(inject([AuthenticationService, XHRBackend], (_service, 
_mockbackend) => { 
    service = _service; 
    mockbackend = _mockbackend; 
    })); 

    it('should set access token in local storage for successful login',() => 
{ 
    const access_token = 'blah83balc380'; 
    const username = 'test'; 
    const currentUserExpected = JSON.stringify({ username: username, token: access_token }); 
    const response = {access_token: access_token}; 
    const responseOptions = new ResponseOptions(); 
    responseOptions.body = JSON.stringify(response); 
    mockbackend.connections.subscribe(connection => { 
     connection.mockRespond(new Response(responseOptions)); 
    }); 
    service.login(username, 'test').subscribe(respond => { 
     expect(respond).toEqual(true); 
     const currentUser = localStorage.getItem('currentUser'); 
     expect(currentUserExpected).toEqual(currentUser); 
    }); 
    }); 

it('should not set access token in local storage for unsuccessful login',() =>  { 
    const username = 'test'; 
    const responseOptions = new ResponseOptions(); 
    responseOptions.body = ''; 
    responseOptions.status = 401; 
    const response = new Response(responseOptions); 
    response.ok = false; 
    response.statusText = 'Unauthorized'; 
    response.type = 2; 

    mockbackend.connections.subscribe(connection => { 
     connection.mockRespond(response); 
    }); 
    service.login(username, 'test').subscribe(respond => { 
     expect(respond).toEqual(false); 
    }, err => { 
     const currentUser = localStorage.getItem('currentUser'); 
     expect(currentUser).toEqual(null); 
    }); 
    }); 
}); 
+0

私はすべてのテストでTestBed.createComponentを使用していましたが、これは必要ありませんでした。 TestBed.createComponentは遅いですが、答えのアプローチは速いです...。十分速く、十分です。 – user372225

0

TestBedは、一般的に角度のサービスをテストするための望ましい方法です。

公式ガイドが言うにもかかわらず、

孤立ユニットテストは、角度または任意の注入された値に依存せず、すべてそれ自体でクラスのインスタンスを調べます。テスターは、newを使用してクラスのテストインスタンスを作成し、必要に応じてコンストラクターパラメーターのテストダブルを提供し、テストインスタンスAPIサーフェスをプローブします。

パイプとサービスの単体テストテストを作成する必要があります。

単離したテストでは、DIテストには対応していません。クラスがnewでインスタンス化されると、そのDIデコレータ(@Injectable,@Inject)はテストされません。

HttpMockBackendが含まれていると、テストの書き方やメンテナンスが容易になります。

パフォーマンスが本当に懸念される場合は、TestBedから隔離されたテストに変換することができます。この場合、Http APIはJasmineモックで複製する必要があります。完全なカバレッジを得るためには、すべての関数呼び出しをテストする必要があります。テストは

mockHttp = jasmine.createSpyObj(['post']); 
    service = new AuthenticationService(mockHttp); 
    ... 

    it(..., fakeAsync(async() => { 
    const bodyMock = { access_token: 'foo' }; 
    const responseMock = { json: jasmine.createSpy().and.returnValue(bodyMock) }; 
    const responseMock$ = Observable.of(responseMock); 
    mockHttp.post.and.returnValue(responseMock$); 

    const login$ = service.login(...); 

    expect(mockHttp.post).toHaveBeenCalledTimes(1); 

    const postArgs = callback.calls.first().args; 
    expect(postArgs).toEqual([..., ..., jasmine.any(RequestOptions)); 

    const requestOptions = postArgs[2]; 
    expect(requestOptions.headers).toEqual(jasmine.any(Headers)); 
    expect(Array.from(requestOptions.headers._headers)).toEqual([ 
     ['Content-Type', ['application/json']], 
     ['accept', ['application/json']] 
    ]); 

    expect(login$).toEqual(jasmine.any(Observable)); 
    const login = await login$.toPromise(); 

    expect(responseMock.json).toHaveBeenCalled(); 
    expect(service.token).toBe('foo'); 
    expect(localStorage.setItem).toHaveBeenCalledWith(...); 
    expect(login).toBe(true); 
    })); 

その後、別のテストがaccess_tokenを持っていないbodyMockで行われるようになります。

localStorageも適切にテストされるためにスタブされるべきであることに注意する必要があります。テスト容易性の理由から、代わりにDI経由でローカルストレージサービスを使用することは有益です。

関連する問題