2

我用angular-cli來創建一個簡單的應用程序來說明我的問題。您可以在此處看到所有代碼: https://github.com/wholladay/tracking您如何測試調用服務的Angular 2指令?

只要包含元素被點擊,該指令就會調用服務。因此,我想嘲笑該服務並確保在向該指令發送點擊事件時調用該服務。

這裏是我的測試代碼:

/* tslint:disable:no-unused-variable */ 
import { inject, addProviders } from '@angular/core/testing'; 
import { TestComponentBuilder } from '@angular/compiler/testing'; 
import { Component } from '@angular/core'; 
import { By } from '@angular/platform-browser'; 
import { TrackingDirective } from './tracking.directive'; 
import { TrackingService } from './tracking.service'; 

class MockTrackingService extends TrackingService { 
    public eventCount = 0; 

    public trackEvent(eventName: string) { 
    this.eventCount++; 
    } 
} 

describe('TrackingDirective',() => { 
    let builder: TestComponentBuilder; 
    let mockTrackingService: MockTrackingService; 
    let trackingDirective: TrackingDirective; 

    beforeEach(() => { 
    mockTrackingService = new MockTrackingService(); 
    trackingDirective = new TrackingDirective(mockTrackingService); 
    addProviders([ 
     {provide: TrackingDirective, use: trackingDirective} 
    ]); 
    }); 

    beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { 
    builder = tcb; 
    })); 

    // General button tests 
    it('should apply class based on color attribute', (done:() => void) => { 
    return builder.createAsync(TestApp).then(fixture => { 
     let testComponent = fixture.debugElement.componentInstance; 
     let buttonDebugElement = fixture.debugElement.query(By.css('button')); 

     buttonDebugElement.nativeElement.click(); 
     expect(buttonDebugElement).toBeTruthy(); 
     expect(mockTrackingService.eventCount).toBe(1); 

     done(); 
    }); 
    }); 
}); 

@Component({ 
    selector: 'test', 
    template: `<button tracking="some button"></button>`, 
    directives: [TrackingDirective] 
}) 
class TestApp { 
} 

這裏是我的指令代碼:

import { Directive, HostListener, Input } from '@angular/core'; 
import { TrackingService } from './tracking.service'; 

@Directive({ 
    selector: '[tracking]', 
    providers: [ 
    TrackingService 
    ] 
}) 
export class TrackingDirective { 

    @Input() tracking: string; 

    constructor(private trackingService: TrackingService) { 
    } 

    @HostListener('click', ['$event.target']) 
    onClick(element) { 
    this.trackingService.trackEvent(this.tracking); 
    } 
} 

當我通過ng test運行測試,測試失敗,因爲EVENTCOUNT仍然是0而不是1 。

回答

2

很好的問題!

正試圖測試有它自己的提供者的指令:

@Directive({ 
    selector: '[tracking]', 
    providers: [ 
    TrackingService 
    ] 
}) 

在這種情況下,我們需要確保,我們MockService的指令,並在我們的測試程序代碼注入。因爲您想檢查eventCount屬性,所以需要在測試代碼中注入。我建議創建MockService的實例,並使用該實例作爲TrackungService值:

let mockService = new MockTrackingService(); 

beforeEach(() => { 
    addProviders([provide(TrackingService, {useValue: mockService})]); 
}); 

MockService需求相同實例作爲供應商對我們的指令:

builder 
     .overrideProviders(TrackingDirective, [provide(TrackingService, {useValue: mockService})]) 
     .createAsync(TestApp).then(fixture => { 

... 

     done(); 
    }); 

參見builder的方法overrideProviders

因此,對於你測試的完整代碼如下所示:

/* tslint:disable:no-unused-variable */ 
import { inject, addProviders } from '@angular/core/testing'; 
import { TestComponentBuilder } from '@angular/compiler/testing'; 
import { Component, provide } from '@angular/core'; 
import { By } from '@angular/platform-browser'; 
import { TrackingDirective } from './tracking.directive'; 
import { TrackingService } from './tracking.service'; 

// do not extend the TrackingService. If there are other 
// dependencies this would be difficult or impossible. 
class MockTrackingService { 
    public eventCount = 0; 

    public trackEvent(eventName: string) { 
    this.eventCount++; 
    } 
} 

let mockService = new MockTrackingService(); 

beforeEach(() => { 
    addProviders([provide(TrackingService, {useValue: mockService})]); 
}); 


describe('TrackingDirective',() => { 
    let builder: TestComponentBuilder; 
    let mockTrackingService: MockTrackingService; 

    beforeEach(inject([TestComponentBuilder, TrackingService], 
    (tcb: TestComponentBuilder, _trackingService: TrackingService) => { 

    builder = tcb; 
    // we need to cast to MockTrackingService because 
    // TrackingService has no eventCount property and we need it 
    mockTrackingService = <MockTrackingService> _trackingService; 

    })); 

    // General button tests 
    it('should apply class based on color attribute', (done:() => void) => { 

    builder 
     .overrideProviders(TrackingDirective, [provide(TrackingService, {useValue: mockService})]) 
     .createAsync(TestApp).then(fixture => { 

     let testComponent = fixture.debugElement.componentInstance; 
     let buttonDebugElement = fixture.debugElement.query(By.css('button')); 

     buttonDebugElement.nativeElement.click(); 

     expect(mockTrackingService.eventCount).toBe(1); 

     done(); 
    }); 
    }); 
}); 

@Component({ 
    selector: 'test', 
    template: `<button tracking="some button"></button>`, 
    directives: [TrackingDirective] 
}) 
class TestApp { 
} 
+0

我的天哪,太感謝你了!我知道我很接近,但我無法弄清楚我失蹤的最後一部分。 – wholladay