Consider the following InjectionToken
for the type Foo
:
export const FOO = new InjectionToken<Foo>(
'foo token',
{ factory: () => new Foo() });
Now assume I was crazy enough to aim for 100% test coverage. To that end I'd have to unit test that little factory
function.
I was thinking to create an injector that has just one provider in my test:
const inj = Injector.create({
providers: [{ provide: FOO }] // compiler error here
});
const foo = inj.get(FOO);
expect(foo).toBeTruthy();
Unfortunately this fails with a compiler error, because { provide: FOO }
is not a valid provider without a useValue
, useFactory
, or useExisting
property. But why am I forced to define one of them when the injection token comes with its own factory?
Of course I tried all options nonetheless:
useValue: FOO
compiles and runs, but doesn't seem to execute the factory methoduseFactory: () => FOO, deps: []
also compiles and runs, but doesn't seem to execute the factory method eitheruseExisting: FOO
compiles, but fails with a circular dependency error during runtimeFunny enough, a similar scenario is presented in the documentation for InjectionToken
, but it doesn't show the registration I'm looking for:
const MY_SERVICE_TOKEN = new InjectionToken<MyService>('Manually constructed MyService', {
providedIn: 'root',
factory: () => new MyService(inject(MyDep)),
});
// How is `MY_SERVICE_TOKEN` token provided?
const instance = injector.get(MY_SERVICE_TOKEN);
I created an example on StackBlitz so you can try yourself.
When you specify factory
function for the InjectionToken
, the token is automatically provided in root. Therefore you don't need to provide it in the test bed either.
In order to use this feature in test, you need to use TestBed
instead of just Injector.create
.
import { TestBed } from '@angular/core/testing';
describe('Foo', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: Foo = TestBed.get(FOO);
expect(service).toBeTruthy();
});
});
The docs say
When creating an
InjectionToken
, you can optionally specify a factory function which returns (possibly by creating) a default value of the parameterized typeT
. This sets up theInjectionToken
using this factory as a provider as if it was defined explicitly in the application's root injector. If the factory function, which takes zero arguments, needs to inject dependencies, it can do so using theinject
function. See below for an example.