Providing mock services in angular 2/4/6 unit test

When you are writing unit tests for a component or a service that depends on another service then it is better to provide test alternatives for that service instead of the real service, as the real service might in turn depend on some other services or it might try to send some request to the server etc. Moreover our purpose is to test the component or service under consideration not its dependencies.

So in this post I am gonna show you how to provide the mock (or spy) service  instance instead of the real one in different scenarios.

First of all we need to create the mock service class or the spy service instance whichever we have decided to use.

Creating a mock service class is fairly easy you just have to create a new class having methods and properties with the same name as in the real class. For example if the actual service class is:-
import { Injectable } from '@angular/core';

@Injectable()
export class CarService {
   constructor() { }

   washCar(){
      ... //actual code
   }
}

The mock class for the above service class can be created as :-
export class CarServiceMock {
   constructor() { }

   washCar(){
      ... //mock code
   }
}

To create a spy service instance you need to call "jasmine.createSpyObj" method as shown below :-
const spy = jasmine.createSpyObj('CarService', ['washCar']);

After creating the mock or spy for your service you need to provide it in place of the actual service. The providing method differs depending on whether the actual service was provided at the module level or at the component level. Both of these methods are described below :-

When actual service is provided at module level :-


When the real service is provided at the module level then its test double should be provided in "TestBed.configureTestingModule()" method as is shown below :-

  • With mock service
    TestBed.configureTestingModule({
        providers: [
           { provide: CarService , useClass: CarServiceMock }
        ]
    });
    

  • With spy service instance
    const spy = jasmine.createSpyObj('CarService', ['washCar']);
    
    TestBed.configureTestingModule({
        providers: [
           { provide: CarService , useValue: spy }
        ]
    });
    

When actual service is provided at component level :-


When the real service s provided at component level, i.e. inside the @Component decorator of the component, then its test double cannot be provided inside "TestBed.configureTestingModule()" method instead it has to be provided in "overrideComponent()" method that is relayed to "TestBed.configureTestingModule()" method, as id shown below :-

  • With mock service
    TestBed.configureTestingModule({            
      declarations: [CarComponent]
    })
    .overrideComponent(CarComponent, {
      set: {
        providers: [
          { provide: CarService, useClass: CarServiceMock } 
        ]
      }
    })
    .compileComponents();
    

  • With spy service instance
    const spy = jasmine.createSpyObj('CarService', ['washCar']);
    
    TestBed.configureTestingModule({            
      declarations: [CarComponent]
    })
    .overrideComponent(CarComponent, {
      set: {
        providers: [
          { provide: CarService, useValue: spy } 
        ]
      }
    })
    .compileComponents();
    

No comments:

Post a Comment