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 test 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 the Jasmine framework with Angular, you need to provide all the required dependencies in the TestBedconfiguration. However, providing all the dependencies may slow down your execution of test cases of applications as you also will need to inject their sub-dependencies.

For example, let's suppose that we are writing the Unit test for XyzComponent, and the template for XyzComponent is following.

<p>
   <app-child-one></app-child-one>
   <app-child-two></app-child-two>
</p>

In the above case, in the TestBed configuration, the user will have to provide all the component dependencies.

describe('Component: Xyz', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [
        XyzComponent,
        XyzChildOneComponent,
        XyzChildTwoComponent
      ],
    });
  });
});

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. ThisNO_ERRORS_SCHEMA also prevents the compiler from telling you about the missing components.

Example

describe('XyzComponent', () => {
    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [
                XyzComponent
            ],
            schemas: [NO_ERRORS_SCHEMA]
        }).compileComponents();
    }));

Mocking the Services

Same as the case of components, you will have to provide 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 that is being used by the component so that you don’t need to inject other dependencies.

Example

// Overriding Functions 
class MockAuthService {
  authenticated = false;
  isAuthenticated() {
    return this.authenticated;
  }
}

// Using Fake Classes 
beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [XyzComponent],
    providers: [{ provide: AuthService, useClass: MockAuthService }],
  }).compileComponents();
}));

In the above example, we are passing the mocked dependency classMockAuthServiceinstead of our actualAuthService.

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 some other implementation.spyOn takes two arguments: the class instance and the string value with the name of the method you want to keep track of, or fake.

Following are some 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.

spyOn(dataService, "someMethod").and.returnValue("some value");
spyOn(dataService, "someMethod").and.returnValues([value1, value2]);
spyOn(dataService, "someMethod").and.returnValue(Observable.of("some value"));
spyOn(dataService, "someMethod").and.returnValue(Promise.resolve("some value"));
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 here.


Similar Articles