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