2016-10-21 12 views
6

サービスを使用するAngular 2コンポーネントをテストしようとしています。サービスにはHttpが注入されています。これはテストには興味がありませんので、サービスのメソッドコールを監視してスパイしようとしています。これはAngular 1での作業には慣れていましたが、Angular 2で作業することはできません。エラーは、Httpのプロバイダがありません。私は実際のサービス方法を模倣するのではなく、実際のサービス方法を監視することに興味があります。ジャスミンスパイによる角2コンポーネントテスト "Httpのプロバイダなし!"エラー

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

import { Component, OnInit } from '@angular/core'; 
import { NavBarLink } from '../../models/nav-bar-link'; 
import { NavBarService } from '../../services/nav-bar/nav-bar.service'; 

@Component({ 
    selector: 'nav-bar', 
    providers: [NavBarService], 
    moduleId: module.id, 
    templateUrl: 'nav-bar.template.html' 
}) 
export class NavBarComponent { 
    constructor(private _navBarService: NavBarService) { } 

links: NavBarLink[]; 

getLinks(): void { 
    this._navBarService.getNavBarLinks().then(data => this.links = data); 
} 

ngOnInit(): void { 
    this.getLinks(); 
    } 
} 

そして、私のサービスは、次のようになります。

import { Injectable } from '@angular/core'; 
import { Headers, Http } from '@angular/http'; 

import 'rxjs/add/operator/toPromise'; 

import { Urls } from '../../constants/urls.constants'; 
import { NavBarLink } from '../../models/nav-bar-link'; 

@Injectable() 
export class NavBarService { 
    constructor(private _http: Http, 
       private _urls: Urls) { } 

    getNavBarLinks():Promise<NavBarLink[]> { 

     return this._http.get(this._urls.NAV_BAR_LINKS)  
      .toPromise() 
      .then(response => { 
       let navLinks = []; 
       for(let navLink of response.json()) { 
        navLinks.push(new NavBarLink(navLink.id,  navLink.description, navLink.route)); 
       } 
       return navLinks; 
      }) 
      .catch(this.handleError); 

    } 

    private handleError(error: any): Promise<any> { 
     console.error('An error occurred', error); // for demo purposes only 
     return Promise.reject(error.message || error); 
    } 

} 

そして最後に私のテストは、この

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

import { NavBarComponent } from './nav-bar.component'; 
import { NavBarService } from '../../services/nav-bar/nav-bar.service'; 

import { Observable } from 'rxjs/Rx'; 

let comp: NavBarComponent; 
let fixture: ComponentFixture<NavBarComponent>; 
let navBarService; 

class MockNavBarService extends NavBarService{ 
    constructor() { 
     super(null, null); 
    } 
} 

describe ('NavBarComponent tests',() => { 

    beforeEach(async(() => { 
     TestBed.configureTestingModule({ 
       declarations: [ NavBarComponent ], 
       providers: [ {provide: NavBarService, useClass:  MockNavBarService} ] 
      }) 
      .compileComponents() 
      .then(createComponent); 
    })); 


    it('should call the getNavBarLinks when ngOnInit is called',() => { 
     comp.ngOnInit(); 
     expect(navBarService.getNavBarLinks).toHaveBeenCalled(); 
    }); 
}); 

function createComponent() { 
    fixture = TestBed.createComponent(NavBarComponent); 
    comp = fixture.componentInstance; 

    navBarService = fixture.debugElement.injector.get(NavBarService); 

    spyOn(navBarService, 'getNavBarLinks').and.returnValue(Promise.resolve([])); 
} 

答えて

0

のように見えるそれは、このためだ

@Component({ 
    providers: [NavBarService], <==== !!!!! 
}) 
export class NavBarComponent { 

@Component.providersは、提供されているものとモジュールレベルよりも優先されます。したがって、Angularは、テストで設定したモックを使用する代わりにNavBarServiceのインスタンスを作成しようとします。

Angularは、@Component.templateのように@Component.providersを上書きすることができます。我々は、次の

TestBed.configureTestingModule({ 
    declarations: [NavBarComponent] 
}) 
.overrideComponent(NavBarComponent, { 
    set: { 
    providers: [ 
     { provide: NavBarService, useClass: MockNavBarService} 
    ] 
    } 
}) 
.compileComponents() 
.then(createComponent) 
0

に問題がMockNavBarServiceがNavBarServiceを拡張することですので、HTTPはまだ提供されると予想されていることを行うことができます。私はこれがなぜそのような技術的な理由を理解していないが、そこにある。継承を削除する場合は、代わりに、一部の缶詰データのObservableを返すモックgetNavBarLinks()を実装することができます。テストでは、NavBarComponentが何らかのメソッドが呼び出されたという事実をテストするのではなく、データで何を行うのかをテストできます。

6

お手数をおかけしていただきありがとうございます。私は適切な領域を探しています。欠けていたのはHttpModuleをインポートすることでした。 Ahmedさんにこれを提案してくれて大変感謝しています。ここに参照のための私の固定のテストではありません:

import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 
import { Provider } from '@angular/core'; 
import { By }    from '@angular/platform-browser'; 
import { DebugElement } from '@angular/core'; 
import { HttpModule } from '@angular/http'; // <==== IMPORT THIS 

import { MockBackend } from '@angular/http/testing'; 
import { NavBarComponent } from './nav-bar.component'; 
import { NavBarService } from '../../services/nav-bar/nav-bar.service'; 
import { Urls } from '../../constants/urls.constants'; 

import { Observable } from 'rxjs/Rx'; 

let comp: NavBarComponent; 
let fixture: ComponentFixture<NavBarComponent>; 
var navBarService; 

describe ('NavBarComponent tests',() => { 

    beforeEach(async(() => { 
     TestBed.configureTestingModule({ 
       declarations: [ NavBarComponent ], 
       imports: [ HttpModule], //<==IMPORT INTO TEST BED 
       providers: [ 
        Urls, 
        MockBackend, 
        NavBarService ] 
      }) 
      .compileComponents() 
      .then(createComponent); 
    })); 


    it('should call the getNavBarLinks when ngOnInit is called',() => { 
     comp.ngOnInit(); 
     expect(navBarService.getNavBarLinks).toHaveBeenCalled(); 
    }); 
}); 

function createComponent() { 
    fixture = TestBed.createComponent(NavBarComponent); 
    comp = fixture.componentInstance; 

    navBarService = fixture.debugElement.injector.get(NavBarService); 

    spyOn(navBarService,  'getNavBarLinks').and.returnValue(Promise.resolve([])); 
} 

モックオブジェクトまたは何のための必要はありません、完璧な

+2

この解決策の問題は、あなたのコンポーネントが消費するサービスの依存関係しているモジュールにもたらしているということですコンポーネントテストに複雑さが増します。 NavBarServiceがプロバイダを変更するたびに、NavBarComponentを使用または使用するすべてのテストファイルを更新する必要があります。元のサービスを拡張しないクラスでサービスを嘲笑することで、依存関係の複雑さが根絶されます。これにより、テストをよりきれいにすることができ、サービスを変更した場合に注意を要することが少なくなります。 – pe8ter

関連する問題