Angular-Karma Solution of NullInjectorError in UnitTest [ionic4]

Jasmine and Karma are used in Unit Testing Angular. Though it has no problems in a Service Class Testing, Component Testing occurs so many errors related to module injection.

In addition, the solution of the errors depends on the error, so it is not only one. It’s very complex.

Setting “providers"

Error as the following.

NullInjectorError: StaticInjectorError(DynamicTestModule)[AppVersion]:

In most cases, the solution are to set modules which are used in module.ts of testing target, to “providers" of TestingModule.

Solution

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ TestPage ],
      providers: [AppVersion]
    })
    .compileComponents();
  }));

RouterTestingModule

Error as the following.

NullInjectorError: StaticInjectorError(DynamicTestModule)[Router -> Location]: 

Solution

If it inject a NavController in PageComponent, the solution is as the following.

  import {RouterTestingModule} from "@angular/router/testing";
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ TestPage ],
      imports: [
        RouterTestingModule
      ]
    })
    .compileComponents();
  }));

TranslateTestingModule

Error as the following.

NullInjectorError: StaticInjectorError(DynamicTestModule)[TranslateService]:

Solution

Create mock as the following.
translate-testing.module

import { Injectable, NgModule, Pipe, PipeTransform } from '@angular/core';
import { TranslateModule, TranslatePipe, TranslateService } from '@ngx-translate/core';
import { Observable, of } from 'rxjs';

const translations: any = {};

@Pipe({
  name: 'translate'
})
export class TranslatePipeMock implements PipeTransform {
  public name = 'translate';

  public transform(query: string, ...args: any[]): any {
    return query;
  }
}

@Injectable()
export class TranslateServiceStub {
  public get(key: T): Observable {
    return of(key);
  }

  public addLangs(langs: string[]) { }

  public setDefaultLang(lang: string) { }

  public getBrowserLang(): string {
    return 'en';
  }

  public use(lang: string) { }
}

@NgModule({
  declarations: [
    TranslatePipeMock
  ],
  providers: [
    {
      provide: TranslateService,
      useClass: TranslateServiceStub
    },
    {
      provide: TranslatePipe,
      useClass: TranslatePipeMock
    },
  ],
  imports: [
    TranslateModule.forRoot({})
  ],
  exports: [
    TranslatePipeMock,
    TranslateModule
  ]
})
export class TranslateTestingModule { }

Set the Mock to “imports" in TestingModule.

  import {TranslateTestingModule} from "@/your-path/translate-testing.module";
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ TestPage ],
      imports: [
        TranslateTestingModule
      ],
    })
    .compileComponents();
  }));

Error @NgModule.entryComponents

In case that target component use other Components.

Error: No component factory found for YOURComponent. Did you add it to @NgModule.entryComponents?

Solution

  beforeEach(async(() => {
    TestBed.configureTestingModule({
	declarations: [YOURComponent],
    }).overrideModule(BrowserDynamicTestingModule, {
      set: {
        entryComponents: [YOURComponent]
      }
    })
    .compileComponents();
  }));

Error ModalController

If it inject ModalController in target Class.

NullInjectorError: StaticInjectorError(DynamicTestModule)[TestPage -> ModalController]:

Solution

  let modalSpy = jasmine.createSpyObj('Modal', ['present']);
  let modalCtrlSpy = jasmine.createSpyObj('ModalController', ['create']);
  modalCtrlSpy.create.and.callFake(function () {
    return modalSpy;
  });
  beforeEach(async(() => {
    TestBed.configureTestingModule({
	providers: [
        {
          provide: ModalController,
          useValue: modalCtrlSpy
        }
      ]
    }).compileComponents();
  }));

Error of APP_BASE_HREF

Error as the following.

Error: No base href set. Please provide a value for the APP_BASE_HREF token or add a base element to the document.

Solution

The error can be resolved with the following.

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      providers: [
        {provide: APP_BASE_HREF, useValue: '/'}
      ]
    }).compileComponents();
  }));