How to mock an @injected service when testing an angular 2 component?
As stated in the comments, Angular needs whatever token is in the @Inject(...)
to be the same as what is given for the provide
property when setting up the service with Angular's DI. This also means that the injection token should be exported outside the module for others to use the @Inject()
syntax.
If the service is provided like this:
@NgModule({
providers: [
{ provide: SERVICE_TOKEN, useClass: Service }
]
})
Then the @Inject()
should be set up like this (using the same injection token):
constructor(@Inject(SERVICE_TOKEN) private service: Service) {
}
So, in your test you mock it out via (again, using the same injection token):
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
{ provide: SOME_TOKEN, useValue: mockService }
]
})
.compileComponents();
}));
For more information on injection tokens, refer to the Angular docs.
Related videos on Youtube
StuperUser
A full stack web app developer most recently using Angular, WebAPI, Dapper. 1.5 years experience in QA and an ISTQB/ISEB Foundation Certificate in Software Testing. BA in Philosophy and Computing from University of Kent. #SOreadytohelp
Updated on June 04, 2022Comments
-
StuperUser almost 2 years
I have a component with the signature:
constructor(private loremApiService: LoremApiService, private ipsumService: IpsumService, private dolorService: DolorService, @Inject('sitService') private sitService: library.service.Service) { }
The spec file for the component is set up with:
let component: PowerBiReportComponent; let fixture: ComponentFixture<TestingComponent>; const mockLoremApi = { methodThatIsCalled: () => {} }; const mockIpsumService = { }; const mockSitService = { }; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ TestingComponent ], schemas: [ CUSTOM_ELEMENTS_SCHEMA ], providers: [ { provide: LoremApiService, useValue: mockLoremApi }, { provide: IpsumService, useValue: mockIpsumService }, UnmockedService, { provide: library.service.Service, useValue: mockSitService } ] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(TestingComponent); component = fixture.componentInstance; fixture.detectChanges(); });
However, the provider is not used, due to the @Inject decorator, the test is failing on run with:
Error: StaticInjectorError(DynamicTestModule)[sitService]: StaticInjectorError(Platform: core)[sitService]: NullInjectorError: No provider for sitService!
How do I force the TestingModule to use the mockSitService despite it being @Injected in the component?
-
Daniel W Strimpel about 6 yearsDid you try to do:
{ provide: 'sitService', useValue: mockSitService }
? -
StuperUser about 6 years@DanielWStrimpel Unfortunately the unanonymised service is imported with
import * as library from 'sit-library';
, so I can't be that specific. Also, tragically, the actual service Type is namedService
-
Daniel W Strimpel about 6 yearsWhatever is in the
@Inject(...)
should be what is given as theprovide
property. Newer versions of Angular require (or strongly recommends?) these to be injection tokens angular.io/guide/dependency-injection#injectiontoken -
StuperUser about 6 years@DanielWStrimpel Ahhh, I've changed the provide object in the test to match the injectionToken in the component's parent module. Now it's erroring with an
[object ErrorEvent] thrown
which may be part of the actual test code failing due to an unimplemented mock. This might fix the issue -
Fateh Mohamed about 6 yearscheck my response here stackoverflow.com/a/49842183/4399281 you can find an example of mocking a service
-
StuperUser about 6 years@DanielWStrimpel If you add ensuring that the injection tokens match in the component's module and the TestModule as the configureTestingModule as an answer, I'll accept that
-
-
EHorodyski over 5 yearsWhat if you have a module, that uses
forRoot
for it's injectable tokens and you want to test one of the services inside of it that utilizes the injected token?MyModule.forRoot(MyModuleConstants)
has a serviceMyModuleService
and hasconstructor(@Inject(MyInjectionToken) constants)
-
Gao Shenghan almost 4 years
useClass
instead ofuseValue
? -
Daniel W Strimpel almost 4 years@GaoShenghan the syntax depends on what you are providing. When setting it up in the module it is using
useClass
so that Angular creates the instance for you. In the test, the OP has variables that are instances of the stubbed services, so you would use theuseValue
version of the provide syntax for those.