Angular TestBed.inject

问题描述 投票:0回答:2

我正在做角度单元测试。 TestBed.inject(service) 是否创建服务的新实例? 我总是假设 TestBed.configureTestingModule() 创建了服务,并且通过注入我们只是访问了所创建服务的引用。

angular unit-testing jasmine angular-dependency-injection
2个回答
10
投票

TestBed.configureTestingModule() 帮助您配置提供程序。配置提供程序意味着您让 Angular 依赖项注入系统了解此依赖项,稍后它可以在通过依赖项注入令牌请求时将其注入到组件中。在下面的示例中,依赖项和令牌是 ValueService。

TestBed.configureTestingModule({ providers: [ValueService] });

稍后,您可以使用 TestBed.inject 在测试中使用 ValueService。当您说 TestBed.inject 时,Angular 依赖注入系统会检查是否有针对令牌注册的提供程序,如果是,则它会创建服务的实例并返回它。如果它没有找到那个令牌,你就会著名的

没有提供 ValueService 错误。

it('should use ValueService', () => {
  service = TestBed.inject(ValueService);
  expect(service.getValue()).toBe('real value');
});

因此,它与我们的应用程序代码类似,我们首先配置提供程序,然后将其注入到我们的组件中以获取该服务的实例。


0
投票

根据一些测试,您的问题的答案似乎是肯定的,虽然

inject
方法的主要目的是返回服务的实例,但它会产生副作用 - 第一次调用它时(因为此时服务实例不存在) - 还实例化服务并将其缓存在模块的注入器内以供后续调用。

测试 API 文档有点简陋,但有一个很好的示例这里

演示应用程序没有对 HeroService 进行单元测试,但我在下面重新创建了一个最小测试。请注意,我添加了两个 console.log 语句:

import { HttpClient } from '@angular/common/http';
import { TestBed } from '@angular/core/testing';
import { HeroService } from './hero.service';
import { MessageService } from './message.service';
describe('HeroService Test', () => {
  let service: HeroService;
  beforeEach(() => {
    console.log("CALL configureTestingModule");
    TestBed.configureTestingModule({
      providers: [HeroService,
        { provide: HttpClient, useValue: jasmine.createSpyObj('HttpClient', ['get']) },
        { provide: MessageService, useValue: jasmine.createSpyObj('MessageService', ['add']) }],
    });
    console.log("CALL inject");
    service = TestBed.inject(HeroService);
  });
  it('should be created', () => { expect(service).toBeTruthy(); });
});

然后修改了HeroService,在其构造函数中添加了控制台日志语句,删除了多余的代码:

export class HeroService {
  private heroesUrl = 'api/heroes';  // URL to web api
  httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' })
  };
  constructor(
    private http: HttpClient,
    private messageService: MessageService) {
      console.log("CREATE HeroService");
    }
  ...

运行测试会在日志中输出以下内容:

✔ Browser application bundle generation complete.
LOG: 'CALL configureTestingModule'
Chrome 128.0.0.0 (Linux x86_64): Executed 0 of 8 SUCCESS (0 secs / 0 secs)
LOG: 'CALL inject'
Chrome 128.0.0.0 (Linux x86_64): Executed 0 of 8 SUCCESS (0 secs / 0 secs)
LOG: 'CREATE HeroService'
Chrome 128.0.0.0 (Linux x86_64): Executed 0 of 8 SUCCESS (0 secs / 0 secs)
Chrome 128.0.0.0 (Linux x86_64): Executed 8 of 8 SUCCESS (0.102 secs / 0.074 secs)
TOTAL: 8 SUCCESS

从日志中可以看出,

HeroService
仅在调用
inject
之后创建,这意味着执行
configureTestingModule
不会实例化服务。不要相信有任何异步执行会影响这个结论。例如。提供者工厂函数不能是异步的。

源代码有明确的答案,但据我所知,这是TestBed模块注入器上的“get”:

const result = this.testModuleRef.injector.get(token, UNDEFINED, convertToBitFlags(flags));

当不在单元测试上下文中运行时,注入器是否以如此懒惰的方式工作,我不能说。

希望有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.