如何提供具有自己的“工厂”的“InjectionToken”?

How to provide an `InjectionToken` that has its own `factory`?

考虑以下 InjectionToken 类型 Foo

export const FOO = new InjectionToken<Foo>(
  'foo token',
  { factory: () => new Foo() });

现在假设我疯狂到以 100% 的测试覆盖率为目标。为此,我必须对那个 factory 函数进行单元测试。

我正在考虑创建一个在我的测试中只有一个提供者的注入器:

const inj = Injector.create({
  providers: [{ provide: FOO }] // compiler error here
});

const foo = inj.get(FOO);

expect(foo).toBeTruthy();

不幸的是,这会因编译器错误而失败,因为 { provide: FOO } 不是没有 useValueuseFactoryuseExisting 属性 的有效提供程序。但是,当注入令牌自带工厂时,为什么我被迫定义其中之一?

当然,我尝试了所有选项:

有趣的是,在 the documentation 中为 InjectionToken 提供了类似的场景,但它没有显示我正在寻找的注册:

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);

我创建了一个example on StackBlitz所以你可以自己试试。

当您为 InjectionToken 指定 factory 函数时,将自动在 root 中提供令牌。所以你也不需要在测试台上提供它。

为了在测试中使用此功能,您需要使用 TestBed 而不是 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

When creating an InjectionToken, you can optionally specify a factory function which returns (possibly by creating) a default value of the parameterized type T. This sets up the InjectionToken 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 the inject function. See below for an example.