Skip to main content

How to test a token in Angular application

For proper testing of tokens in Angular application, we need extra declarations compare to their usage in the application.

Because a token might have a factory function, it is not always necessary to list the token in providers for successful execution of its application. Unfortunately, for testing it is not like that, and in this case ng-mocks cannot detect the token. Please make sure, that the token and its dependencies are listed in providers of a related module, then ng-mocks can mock them properly.

Configuration of TestBed should be done via MockBuilder where its first parameter is the token we want to test, and the second parameter is its module.

beforeEach(() => MockBuilder(TOKEN_EXISTING, TargetModule));

If you test a token with useExisting flag, then you need to keep in mind that the pointer will be mocked unless it has been marked for being kept.

beforeEach(() =>  MockBuilder(TOKEN_EXISTING, TargetModule).keep(ServiceExisting));

In a test we need to fetch the token and assert its value.

const token = TestBed.get(TOKEN_EXISTING);expect(token).toEqual(jasmine.any(ServiceExisting));

Live example#

https://github.com/ike18t/ng-mocks/blob/master/examples/TestToken/test.spec.ts
import { Injectable, InjectionToken, NgModule } from '@angular/core';import { TestBed } from '@angular/core/testing';import { MockBuilder, ngMocks } from 'ng-mocks';
const TOKEN_CLASS = new InjectionToken('CLASS');const TOKEN_EXISTING = new InjectionToken('EXISTING');const TOKEN_FACTORY = new InjectionToken('FACTORY');const TOKEN_VALUE = new InjectionToken('VALUE');
class ServiceClass {  public readonly name = 'class';}
@Injectable()class ServiceExisting {  public readonly name = 'existing';}
// A module that provides all services.@NgModule({  providers: [    ServiceExisting,    {      provide: TOKEN_CLASS,      useClass: ServiceClass,    },    {      provide: TOKEN_EXISTING,      useExisting: ServiceExisting,    },    {      provide: TOKEN_FACTORY,      useFactory: () => 'FACTORY',    },    {      provide: TOKEN_VALUE,      useValue: 'VALUE',    },  ],})class TargetModule {}
describe('TestToken', () => {  ngMocks.faster();
  // Because we want to test the tokens, we pass them in .keep in  // the chain on MockBuilder. To correctly satisfy their  // initialization we need to pass its module as the second  // parameter.  beforeEach(() => {    return MockBuilder()      .mock(TargetModule)      .keep(TOKEN_CLASS)      .keep(TOKEN_EXISTING)      .keep(TOKEN_FACTORY)      .keep(TOKEN_VALUE);  });
  it('creates TOKEN_CLASS', () => {    const token = TestBed.get(TOKEN_CLASS);
    // Verifying that the token is an instance of ServiceClass.    expect(token).toEqual(jasmine.any(ServiceClass));    expect(token.name).toEqual('class');  });
  it('creates TOKEN_EXISTING', () => {    const token = TestBed.get(TOKEN_EXISTING);
    // Verifying that the token is an instance of ServiceExisting.    // But because it has been replaced with a mock copy,    // we should see an empty name.    expect(token).toEqual(jasmine.any(ServiceExisting));    expect(token.name).toBeUndefined();  });
  it('creates TOKEN_FACTORY', () => {    const token = TestBed.get(TOKEN_FACTORY);
    // Checking that we have here what factory has been created.    expect(token).toEqual('FACTORY');  });
  it('creates TOKEN_VALUE', () => {    const token = TestBed.get(TOKEN_VALUE);
    // Checking the set value.    expect(token).toEqual('VALUE');  });});