How do you test Angular 2 directive that calls a service?

wholladay

I used angular-cli to create a simple app to illustrate my problem. You can see all the code here: https://github.com/wholladay/tracking

The directive calls a service whenever the containing element is clicked on. Therefore, I'd like to mock the service and ensure that it is called when a click event is sent to the directive.

Here is my test code:

/* 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 {
}

And here is my directive code:

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);
  }
}

When I run the tests via ng test, the test fails because eventCount is still 0 instead of 1.

michael

great question!

You are trying to test a directive that has it's own provider:

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

In this case we need to make sure, that our MockService is injected in the directive and in our test code. Injecting in the test code is required because you want to check the eventCount property. I would suggest creating an Instance of the MockService and use this instance as a value for the TrackungService:

let mockService = new MockTrackingService();

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

The same instance of the MockService needs to be used as provider for our directive:

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

...

      done();
    });

See the method overrideProviders of the builder.

So the complete code for your test looks like this:

/* 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 {
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

How do you update model from directive in Angular 2?

From Dev

How do you inject an angular2 service into a unit test? (RC3)

From Dev

How to Unit Test a Directive In Angular 2?

From Dev

Angular unit test that calls a service

From Dev

Angular unit test that calls a service

From Dev

Dart - How do you write a test against a function that calls exit()?

From Dev

How do you write a test against a function that calls exit()?

From Dev

How do you write a test against a function that calls exit()?

From Dev

How do you test a service in spring mvc with mockito, rxjava2 using an io scheduler?

From Dev

In Angular 2+, how do you specify that the native element should be bound to the template variable if it also has an Angular directive applied to it?

From Dev

How do I test the HTML pointed to by the templateUrl of an angular directive?

From Dev

How do I trigger a click event in a unit test for an Angular directive

From Dev

How can I test this Angular 2 service?

From Dev

How do I return the results of multiple asynchronous calls in Angular service

From Dev

How do you test service or controller methods in grails 2.3

From Dev

How do you test service or controller methods in grails 2.3

From Dev

How do you call a web service that returns single JSON object in Angular 2?

From Dev

How do I do a jasmine unit test for my angular directive that needs to test its emit?

From Dev

How do you display a literal {{ in Angular 2

From Dev

How do you properly configure a provider service in Angular.js

From Dev

How do you add a service in Angular so a controller can use it?

From Dev

How do you properly configure a provider service in Angular.js

From Dev

Angular 2: Directive Subscribe To Observables of a service

From Dev

how to test an AngularJS service with rest calls in Jasmin

From Dev

How do you mock classes that are used in a service that you're trying to unit test using JUnit + Mockito

From Dev

How do you 'require' an attribute on an angularjs directive?

From Dev

How do you check if & variable is set in directive

From Dev

Angular How to control a directive from a service?

From Dev

How to create a directive inside a service in angular js?

Related Related

  1. 1

    How do you update model from directive in Angular 2?

  2. 2

    How do you inject an angular2 service into a unit test? (RC3)

  3. 3

    How to Unit Test a Directive In Angular 2?

  4. 4

    Angular unit test that calls a service

  5. 5

    Angular unit test that calls a service

  6. 6

    Dart - How do you write a test against a function that calls exit()?

  7. 7

    How do you write a test against a function that calls exit()?

  8. 8

    How do you write a test against a function that calls exit()?

  9. 9

    How do you test a service in spring mvc with mockito, rxjava2 using an io scheduler?

  10. 10

    In Angular 2+, how do you specify that the native element should be bound to the template variable if it also has an Angular directive applied to it?

  11. 11

    How do I test the HTML pointed to by the templateUrl of an angular directive?

  12. 12

    How do I trigger a click event in a unit test for an Angular directive

  13. 13

    How can I test this Angular 2 service?

  14. 14

    How do I return the results of multiple asynchronous calls in Angular service

  15. 15

    How do you test service or controller methods in grails 2.3

  16. 16

    How do you test service or controller methods in grails 2.3

  17. 17

    How do you call a web service that returns single JSON object in Angular 2?

  18. 18

    How do I do a jasmine unit test for my angular directive that needs to test its emit?

  19. 19

    How do you display a literal {{ in Angular 2

  20. 20

    How do you properly configure a provider service in Angular.js

  21. 21

    How do you add a service in Angular so a controller can use it?

  22. 22

    How do you properly configure a provider service in Angular.js

  23. 23

    Angular 2: Directive Subscribe To Observables of a service

  24. 24

    how to test an AngularJS service with rest calls in Jasmin

  25. 25

    How do you mock classes that are used in a service that you're trying to unit test using JUnit + Mockito

  26. 26

    How do you 'require' an attribute on an angularjs directive?

  27. 27

    How do you check if & variable is set in directive

  28. 28

    Angular How to control a directive from a service?

  29. 29

    How to create a directive inside a service in angular js?

HotTag

Archive