2016-03-01 12 views
16

Befolgen Sie das Codebeispiel auf Ari Lerner ng-book2, und mit Angular 2 Beta 7, versuche ich, einen Anruf zu einem Dienst erfolglos zu verspotten und auszuspionieren.Wie man einen Serviceanruf in Angular2 ausspioniert

Dies ist die Hauptkomponente mit dem Service:

benutzer list.component.ts

import {Component, OnInit} from 'angular2/core'; 
import {UserService} from './user.service'; 
import {IUser} from './user.model'; 

@Component({ 
    selector: 'user-list', 
    providers: [UserService], 
    template: ` 
    <div *ngFor="#user of users" class="user"> 
     <span class="username">Username: {{ user.username }}</span><br> 
     <span class="email">Email: {{ user.email }}</span> 
    </div> 
    ` 
}) 
export class UserListComponent implements OnInit { 
    public users: IUser[]; 
    private userService: UserService; 

    constructor(userService: UserService) { 
    this.userService = userService; 
    } 

    ngOnInit(): void { 
    this.userService.getAllUsers().subscribe(
     (users: IUser[]) => { 
     this.users = users; 
     }, 
     (error: any) => { 
     console.log(error); 
     } 
    ); 
    } 
} 

Und das ist der Dienst selbst.

user.service.ts

import {Injectable} from 'angular2/core'; 
import {Http} from 'angular2/http'; 
import {Observable} from 'rxjs/Observable'; 
import 'rxjs/Rx'; 
import {IUser} from './user.model'; 

@Injectable() 
export class UserService { 
    private http: Http; 
    private baseUrl: string = 'http://jsonplaceholder.typicode.com/users'; 

    constructor(http: Http) { 
    this.http = http; 
    } 

    public getAllUsers(): Observable<IUser[]> { 
    return this.http.get(this.baseUrl) 
     .map(res => res.json()); 
    } 
} 

Um die UserListComponent zu testen, ich versuche, die UserService und auszuspionieren seinen Methodenaufruf getAllUser mit dem folgenden Code zu verspotten:

Benutzer -list.component.spec.ts

import { 
    describe, 
    expect, 
    it, 
    injectAsync, 
    TestComponentBuilder, 
    ComponentFixture, 
    setBaseTestProviders, 
} from 'angular2/testing'; 

import {SpyObject} from 'angular2/testing_internal'; 

import { 
    TEST_BROWSER_PLATFORM_PROVIDERS, 
    TEST_BROWSER_APPLICATION_PROVIDERS 
} from 'angular2/platform/testing/browser'; 

import {provide} from 'angular2/core'; 

import {UserListComponent} from './user-list.component'; 
import {UserService} from './user.service'; 

class SpyUserService extends SpyObject { 
    public getAllUsers: Function; 
    public fakeResponse: any = null; 

    constructor() { 
    super(UserService); 
    this.getAllUsers = this.spy('getAllUsers').andReturn(this); 
    } 

    public subscribe(callback) { 
    callback(this.fakeResponse); 
    } 

    public setResponse(data: any): void { 
    this.fakeResponse = data; 
    } 
} 

describe('When rendering the UserListComponent and mocking the UserService',() => { 

    setBaseTestProviders(TEST_BROWSER_PLATFORM_PROVIDERS, TEST_BROWSER_APPLICATION_PROVIDERS); 

    it('should show one mocked user', injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => { 

    let spyUserService = new SpyUserService(); 
    spyUserService.setResponse([{ 
     username: 'ryan', 
     email: '[email protected]' 
    }]); 

    return tcb 
     .overrideProviders(UserListComponent, [provide(UserService, {useValue: spyUserService})]) 
     .createAsync(UserListComponent) 
     .then((fixture: ComponentFixture) => { 
     fixture.detectChanges(); 
     expect(spyUserService.getAllUsers).toHaveBeenCalled(); 
     }); 
    })); 

}); 

Wenn Karma mit dem Test Ich erhalte die folgende Konsole Fehler auszuführen:

Chrome 48.0.2564 (Mac OS X 10.11.3) ERROR 
    Uncaught TypeError: Cannot read property 'isSlow' of null 
    at /Users/david/apps/sandbox/angular2-testing-cookbook/src/tests.entry.ts:19430 

Sie jemand wissen, warum dieser Fehler wird geworfen oder der richtige Weg, einen Dienst für Einheit zu verspotten und auszuspionieren eine Angular 2 Testen Komponente?

+0

Wissen Sie, wo 'isSlow' herkommt, der Code in der Frage enthält es nicht. –

+0

Kommt aus der Datei 'testing_internal' –

Antwort

20

Ich nehme einen etwas anderen Ansatz und verwenden selbst injizieren halten, die Dienstinstanz durch DI statt zu bekommen. So würde Ihr Test wie folgt aussehen:

import { 
    describe, 
    expect, 
    it, 
    tick, 
    inject, 
    fakeAsync, 
    TestComponentBuilder, 
    ComponentFixture, 
    addProviders 
} from 'angular2/testing'; 

import { Component, provide } from '@angular/core'; 

import {UserListComponent} from './user-list.component'; 

import {UserService} from './user.service'; 
import {MockUserService} from './user.service.mock'; 

describe('When loading the UserListComponent',() => { 

    beforeEach(() => addProviders([ 
     {provide: UserService, useClass: MockUserService} 
    ])); 

    it('should call the getAllUsers method from the UserService', 
    inject([TestComponentBuilder, UserService], fakeAsync((tcb: TestComponentBuilder, mockUserService: UserService) => { 
     spyOn(mockUserService, 'getAllUsers'); 

     tcb 
     .createAsync(UserListComponent) 
     .then((fixture: ComponentFixture) => { 
      tick(); 
      fixture.detectChanges(); 
      expect(mockUserService.getAllUsers).toHaveBeenCalled(); 
     }); 
    })) 
); 

    it('should show one mocked user', 
    inject([TestComponentBuilder, UserService], fakeAsync((tcb: TestComponentBuilder, mockUserService: UserService) => { 
     mockUserService.setResponse([{ 
     username: 'ryan', 
     email: '[email protected]' 
     }]); 

     tcb 
     .createAsync(UserListComponent) 
     .then((fixture: ComponentFixture) => { 
      tick(); 
      fixture.detectChanges(); 
      let compiled = fixture.debugElement.nativeElement; 
      expect(compiled.querySelector('div:nth-child(1) .username')).toHaveText('Username: ryan'); 
      expect(compiled.querySelector('div:nth-child(1) .email')).toHaveText('Email: [email protected]'); 
     }); 
    })) 
); 

}); 

Edit für Angular 4

Die neueste docs haben eine einfachere Art und Weise halten, einen Dienst des Erhaltens des Injektors Komponente:

fixture = TestBed.createComponent(TestComponent); 
    component = fixture.componentInstance; 
    const mockService = fixture.debugElement.injector.get(MyService); 
+0

Dies ist ein besserer Ansatz, danke –

+0

Ist das Angular2? Darf ich Sie fragen, wie Sie diese SpyOn-Funktion bekommen? Ich verwende "@ angular/core": "2.0.0-rc.3", und weiß nicht, woher ich es importieren kann, um es zu benutzen. Kann jemand eine Ressource/ein Beispiel/eine Dokumentation vorschlagen? –

+1

@ RogérioRodriguesdeAlcântara spyOn ist Teil von [Jasmine] (http://jasmine.github.io/2.0/introduction.html). Es gibt ein Beispielprojekt [hier] (https://github.com/juliemr/ng2-test-seed), das ich für gut befunden habe und das auch [this] (https://www.youtube. com/watch? v = C0F2E-PRm44 & feature = youtu.be & list = PLTqvbBh8rJwhFiIriRie3FzyD_CXtHAf) Tutorial dazu. – JayChase

4

fand ich die Lösung

test.entry.ts

import {setBaseTestProviders} from 'angular2/testing'; 

import { 
    TEST_BROWSER_PLATFORM_PROVIDERS, 
    TEST_BROWSER_APPLICATION_PROVIDERS 
} from 'angular2/platform/testing/browser'; 

setBaseTestProviders(TEST_BROWSER_PLATFORM_PROVIDERS, TEST_BROWSER_APPLICATION_PROVIDERS); 

import './user-list.component.spec'; 

benutzer list.component.spec.ts

import { 
    describe, 
    expect, 
    it, 
    tick, 
    inject, 
    fakeAsync, 
    TestComponentBuilder, 
    ComponentFixture, 
    beforeEachProviders 
} from 'angular2/testing'; 

import {UserListComponent} from './user-list.component'; 
import {MockUserService} from './user.service.mock'; 

describe('When loading the UserListComponent',() => { 

    let mockUserService: MockUserService; 

    beforeEachProviders(() => { 
    mockUserService = new MockUserService(); 
    return [mockUserService.getProvider()]; 
    }); 

    it('should call the getAllUsers method from the UserService', 
    inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => { 
     spyOn(mockUserService, 'getAllUsers'); 

     tcb 
     .createAsync(UserListComponent) 
     .then((fixture: ComponentFixture) => { 
      tick(); 
      fixture.detectChanges(); 
      expect(mockUserService.getAllUsers).toHaveBeenCalled(); 
     }); 
    })) 
); 

    it('should show one mocked user', 
    inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => { 
     mockUserService.setResponse([{ 
     username: 'ryan', 
     email: '[email protected]' 
     }]); 

     tcb 
     .createAsync(UserListComponent) 
     .then((fixture: ComponentFixture) => { 
      tick(); 
      fixture.detectChanges(); 
      let compiled = fixture.debugElement.nativeElement; 
      expect(compiled.querySelector('div:nth-child(1) .username')).toHaveText('Username: ryan'); 
      expect(compiled.querySelector('div:nth-child(1) .email')).toHaveText('Email: [email protected]'); 
     }); 
    })) 
); 

}); 

Benutzer. service.mock.ts

import {provide, Provider} from 'angular2/core'; 
import {UserService} from './user.service'; 
import * as Rx from 'rxjs/Rx'; 

export class MockUserService { 

    public fakeResponse: any = null; 

    public getAllUsers(): Rx.Observable<any> { 
    let subject = new Rx.ReplaySubject() 
    subject.next(this.fakeResponse); 
    return subject; 
    } 

    public setResponse(response: any): void { 
    this.fakeResponse = response; 
    } 

    public getProvider(): Provider { 
    return provide(UserService, {useValue: this}); 
    } 
} 
4

Für alle Neulinge zum Testen in Winkel 2 (wie ich hier)

Die createSpy oder SpyOn Methoden sind nur verfügbar, wenn Sie die richtigen Typisierungen für Jasmin installiert haben. Andernfalls wird es einen Typoskript-Fehler werfen.

Check in Ihrer typings.json Datei und wenn nicht vorhanden ist, führen Sie diese

Typisierungen installieren --save --global Registrierung: dt/Jasmin

3

Die anderen Lösungen nicht für mich arbeiten, damit ich spritzte den Dienst mit injector von debugElement.

import { TestBed, 
     async } from '@angular/core/testing'; 

@injectable() 
class MyService { 
    public method() {} 
} 

let MyMockedService = { 
    method:() => {} 
} 

@Component({ 
    template: '' 
}) 
class MyComponent { 
    constructor(private myService: MyService) {;} 
    public method() { 
    this.myService.method(); 
    } 
} 

describe('Test',() => { 
    beforeEach(async(() => { 
    TestBed 
     .configureTestingModule({ 
     imports: [ 
      CommonModule 
     ], 
     declarations: [ 
      MyComponent 
     ], 
      providers: [ 
      { provide: MyService, useValue: MyMockedService} 
     ] 
     }) 
     .compileComponents() 
     .then(() => { 
     fixture = TestBed.createComponent(MyComponent); 
     myComponent = fixture.componentInstance; 
     }); 
    })); 
    it('should spy on service',() => { 
    let myMockedService = fixture.debugElement.injector.get(MyMockedService); 
    spyOn(myMockedService, 'method'); 
    myComponent.method(); 
    expect(myMockedService.method); 
    }); 
}) 
Verwandte Themen