Search code examples
angulartestbed

Angular unit tests for services that uses TranslateService


How do I have to use the TranslateService in unit tests of a service? TranslateService is used in the service class in that way:

export class ErrorControllerService {
    constructor(public translate: TranslateService) {
    }
    ...
}

And I tried to configure the TestingModule TestBed in that way:

import { MyService } from './myservice';
import { TestBed } from '@angular/core/testing';

describe('MyService', () => {
  let myService:MyService;
  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ ],
      imports: [
                TranslateModule.forRoot({
                    loader: {
                        provide: TranslateLoader,
                        useFactory: HttpLoaderFactory,
                        deps: [HttpClient]
                    }
                    }),
      ],
      providers: [ MyService, TranslateService ],
    });
    let myService = TestBed.get(MyService);
  });

  it('should be created', () => {
    expect(myService).toBeDefined();
  });
});

During running of npm test I get this message

NullInjectorError: StaticInjectorError(DynamicTestModule)[TranslateService]: 
  StaticInjectorError(Platform: core)[TranslateService]: 
    NullInjectorError: No provider for TranslateService!
error properties: Object({ ngTempTokenPath: null, ngTokenPath: [ Function ] })
    at <Jasmine>
    at NullInjector.get (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:778:1)
    at resolveToken (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:2564:1)
    at tryResolveToken (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:2490:1)
    at StaticInjector.get (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:2353:1)
    at resolveToken (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:2564:1)
    at tryResolveToken (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:2490:1)
    at StaticInjector.get (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:2353:1)
    at resolveNgModuleDep (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:26403:1)
    at NgModuleRef_.get (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:27491:1)
    at injectInjectorOnly (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:657:1)

Any idea, what I do wrong? My research showed me just examples with a component, but not with a service.


Solution

  • I think I took this from another answer here on SO but I'm unable to find it, so...

    The solution is to build your TranslateTestingModule

    I have mocks for the pipe, and the different functions of the service this has worked wonders for me so far.

    import { Injectable, NgModule, Pipe, PipeTransform } from '@angular/core';
    import {
      TranslateLoader,
      TranslateModule,
      TranslatePipe,
      TranslateService
    } from '@ngx-translate/core';
    
    import { Observable, of } from 'rxjs';
    
    const translations: any = {};
    
    class FakeLoader implements TranslateLoader {
      getTranslation(lang: string): Observable<any> {
        return of(translations);
      }
    }
    
    @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<T>(key: T): Observable<T> {
        return of(key);
      }
    
      public getBrowserLang() {
        return 'es';
      }
    
      public setDefaultLang(language) {}
    
      public get currentLang() {
        return 'en';
      }
    
      public instant(key) {
        return 'value';
      }
    
      public use(lang) {}
    }
    
    @NgModule({
      declarations: [TranslatePipeMock],
      providers: [
        { provide: TranslateService, useClass: TranslateServiceStub },
        { provide: TranslatePipe, useClass: TranslatePipeMock }
      ],
      imports: [
        TranslateModule.forRoot({
          loader: { provide: TranslateLoader, useClass: FakeLoader }
        })
      ],
      exports: [TranslatePipeMock, TranslateModule]
    })
    export class TranslateTestingModule {}
    

    And then you just add it to the imports section of your testing module