Mocking Dependencies In Angular Unit Tests

There could be 3 different scenarios when it comes to mocking the dependencies in order to optimize the performance of unit tests cases and to reduce the development time of unit test cases.
  • Mocking the Components
  • Mocking the Services
  • Mocking or Spying the Methods
Let's discuss all of the above one by one.
 

Mocking the components

 
When using Jasmine framework with Angular, you need to provide all the required dependencies in the TestBed configuration. But providing all the dependencies may slow down your execution of test cases of applications as you also will need to inject their sub-dependencies too.
 
For example, let's suppose that we are writing the Unit test for XyzComponent, and the template for XyzComponent is following,
  1. <div>    
  2.    <app-child-one> </app-child-one>    
  3.    <app-child-two> </app-child-two>    
  4. </div>   
In the above case, in the TestBed configuration, the user will have to provide the all the component dependencies. 
  1. describe('Component: Xyz', () => {    
  2. beforeEach(() => {    
  3.     TestBed.configureTestingModule({    
  4.         declarations: [   
  5.             XyzComponent,   
  6.             XyzChildOneComponent,   
  7.             XyzChildTwoComponent   
  8.          ],    
  9.         });    
  10.      });    
  11.   });    
Look at the above part, we have injected or provided dependencies of all the components. If these components have their own component dependencies, then you will have to provide those dependencies too. So, injecting or providing all the dependencies is time consuming and will degrade the performance too.

In order to avoid this declaration of the different components, we can define the NO_ERRORS_SCHEMA, which will skip the check of all the component dependencies. This NO_ERRORS_SCHEMA also prevents the compiler from telling you about the missing components.
 
Example
  1. describe('XyzComponent', () => {    
  2.     beforeEach(async(() => {    
  3.         TestBed.configureTestingModule({    
  4.             declarations: [    
  5.                 XyzComponent    
  6.             ],    
  7.             schemas: [NO_ERRORS_SCHEMA]    
  8.           }).compileComponents();    
  9.   }));    

Mocking the Services


Same as the case of components, you will have to provide the all the indirectly injected dependencies too. Let's understand it with the following example:
Suppose in your XyzComponent you have used the AuthService for authentication purposes. AuthService internally uses 3 different services (for ex: LoginService, SignupService and CommonService) for different scenarios. So, in this scenario, when writing the unit test case for XyzComponent, you will have to inject the dependencies of all 4 services; i.e. AuthService, LoginService, SignupService, and CommonService.
 
It may happen that CommonService internally uses some other services too. In that scenario, you will have to inject those dependencies too.

To avoid scenarios like this, where you need to inject so many dependencies, you can mock only the service which is being used by the component, so that you don’t need to inject other dependencies.

Example
  1. // Overriding Functions   
  2. class MockAuthService {    
  3.   authenticated = false;    
  4.   isAuthenticated() {     
  5.     return this.authenticated;    
  6.   }    
  7. }    
  8.     
  9. // Using Fake Classes      
  10. beforeEach(async(() => {    
  11.   TestBed.configureTestingModule({    
  12.     declarations: [XyzComponent],    
  13.     providers: [{ provide: AuthService, useClass: MockAuthService }],    
  14.   }).compileComponents();    
  15. }));    
In the above example, we are passing the mocked dependency class MockAuthService instead of our actual AuthService,
 

Mocking or Spying Methods

 
spyOn is the API provided by the Jasmine framework which is used for keeping track of the method. It can also be used for mocking the method or faking the method with the some other implementation. spyOn takes the two arguments: the class instance and the string value with the name of the method you want to keep the track on, or fake. 
 
Following are some of the examples of how you can use spyOn to mock your method to return specific data or to call fake the dummy implementation instead of calling your actual method.
  1. spyOn(dataService, “someMethod”). and.returnValue(“some value");    
  2. spyOn(dataService, “someMethod”). and.returnValues([value1, value2]);    
  3. spyOn(dataService, “someMethod”) and.returnValue(Observable.of(“some value”));    
  4. spyOn(dataService, “someMethod”) and.returnValue(Promise.resolve(“some value”));    
  5. spyOn(dataService, 'getBankAccountData').and.callFake(()=>{ return “dummyData”});    
Thanks for reading this article and any suggestions are really appreciated. You can check out these unit test examples and some other samples at here.