如何测试调用服务的Angular 2指令?

如何测试调用服务的Angular 2指令?,angular,angular2-directives,angular2-services,angular2-testing,Angular,Angular2 Directives,Angular2 Services,Angular2 Testing,我使用angular cli创建了一个简单的应用程序来说明我的问题。您可以在此处看到所有代码: 只要单击包含元素,该指令就会调用服务。因此,我希望模拟该服务,并确保在向指令发送单击事件时调用该服务 以下是我的测试代码: /* tslint:disable:no-unused-variable */ import { inject, addProviders } from '@angular/core/testing'; import { TestComponentBuilder } from

我使用angular cli创建了一个简单的应用程序来说明我的问题。您可以在此处看到所有代码:

只要单击包含元素,该指令就会调用服务。因此,我希望模拟该服务,并确保在向指令发送单击事件时调用该服务

以下是我的测试代码:

/* 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 {
}
当我通过
ng test
运行测试时,测试失败,因为eventCount仍然是0而不是1。

好问题

您正在尝试测试具有自己的提供程序的指令:

@指令({
选择器:“[跟踪]”,
供应商:[
跟踪服务
]
})
在这种情况下,我们需要确保将
MockService
注入指令和测试代码中。需要注入测试代码,因为您要检查
eventCount
属性。我建议创建
MockService
的实例,并将此实例用作
TrackungService
的值:

let mockService=new MockTrackingService();
在每个(()=>{
addProviders([Provider(TrackingService,{useValue:mockService}]);
});
相同的
MockService
实例需要用作我们指令的提供者:

builder
.overrideProviders(TrackingDirective,[Provider(TrackingService,{useValue:mockService})])
.createAsync(TestApp).then(fixture=>{
...
完成();
});
请参见
builder
的方法
overrideProviders

因此,测试的完整代码如下所示:

/*tslint:disable:没有未使用的变量*/
从“@angular/core/testing”导入{inject,addProviders};
从'@angular/compiler/testing'导入{TestComponentBuilder};
从“@angular/core”导入{组件,提供};
从“@angular/platform browser”导入{By}”;
从“./tracking.directive”导入{TrackingDirective};
从“/tracking.service”导入{TrackingService};
//不要扩展TrackingService。如果还有其他
//这将是困难或不可能的。
类MockTrackingService{
公共事件计数=0;
公共跟踪事件(事件名称:字符串){
这个.eventCount++;
}
}
让mockService=new MockTrackingService();
在每个(()=>{
addProviders([Provider(TrackingService,{useValue:mockService}]);
});
描述('TrackingDirective',()=>{
let生成器:TestComponentBuilder;
让mockTrackingService:mockTrackingService;
每次之前(注入([TestComponentBuilder,TrackingService],
(tcb:TestComponentBuilder,_trackingService:trackingService)=>{
builder=tcb;
//我们需要转换到MockTrackingService,因为
//TrackingService没有eventCount属性,我们需要它
mockTrackingService=\u trackingService;
}));
//一般按钮测试
它('应基于颜色属性应用类',(完成:()=>void)=>{
建设者
.overrideProviders(TrackingDirective,[Provider(TrackingService,{useValue:mockService})])
.createAsync(TestApp).then(fixture=>{
设testComponent=fixture.debugElement.componentInstance;
让buttonDebugElement=fixture.debugElement.query(By.css('button'));
buttonDebugElement.nativeElement.click();
expect(mockTrackingService.eventCount).toBe(1);
完成();
});
});
});
@组成部分({
选择器:“测试”,
模板:``,
指令:[跟踪指令]
})
类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);
  }
}