You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不使用TestBed时,如何获取forwardRef的Jest测试覆盖率?

问题

我们有一个带自定义装饰器的表单控件组件,代码如下:

@Component({
  selector: 'value-selector',
  templateUrl: './selector.component.html',
  styleUrls: ['./selector.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => ValueSelectorComponent),
      multi: true
    }
  ]
})

更新babel等依赖后,必须把提供者从useExisting: ValueSelectorComponent改成useExisting: forwardRef(() => ValueSelectorComponent)。

我们的Jest测试通过beforeEach调用instantiate方法创建组件实例:

function instantiate({
  eventTypeService = {},
  translationService = {}
}): ValueSelectorComponent {
  return new ValueSelectorComponent(
    eventTypeService as EventTypeService,
    translationService as TranslateService
  );
}

describe(ValueSelectorComponent.name, () => {
  let component: ValueSelectorComponent;
  // 省略部分代码
  
  beforeEach(() => {
    mockTranslateGet = jest.fn().mockReturnValue(of());
    mockTranslationService = {
      get: mockTranslateGet
    } as unknown as TranslateService;

    getAllSpy = jest.fn();
    mockEventTypeService = {
      getAll: getAllSpy
    } as unknown as EventTypeService;
    
    // 省略部分代码
    component = instantiate({
      eventTypeService: mockEventTypeService,
      translationService: mockTranslationService
    });
  });

现在无法覆盖useExisting里forwardRef(() => ValueSelectorComponent)这行代码的覆盖率,找到的方案都要用TestBed,但我们不想用TestBed,请问不用TestBed怎么拿到这行的覆盖率?

解决方案

1. 手动触发forwardRef的回调函数

forwardRef本质是返回一个包含回调的对象,你可以直接在测试里调用这个回调,触发代码执行:

it('should cover forwardRef callback in providers', () => {
  // 从组件装饰器元数据中取出NG_VALUE_ACCESSOR提供者
  const providers = (ValueSelectorComponent as any).__annotations__[0].providers;
  const ngValueAccessorProvider = providers.find(p => p.provide === NG_VALUE_ACCESSOR);
  // 调用forwardRef对应的回调函数
  ngValueAccessorProvider.useExisting();
});

这段代码会直接执行forwardRef包裹的箭头函数,从而覆盖目标代码行。

2. 用Angular内置方法解析forwardRef

Angular内部用resolveForwardRef方法处理forwardRef,你可以手动导入并调用这个方法,既贴近真实逻辑又能覆盖代码:

import { resolveForwardRef } from '@angular/core';

it('should resolve forwardRef for NG_VALUE_ACCESSOR', () => {
  const providers = (ValueSelectorComponent as any).__annotations__[0].providers;
  const ngValueAccessorProvider = providers.find(p => p.provide === NG_VALUE_ACCESSOR);
  // 解析forwardRef并验证结果
  const resolvedValue = resolveForwardRef(ngValueAccessorProvider.useExisting);
  expect(resolvedValue).toBe(ValueSelectorComponent);
});

注意事项

  • TypeScript编译后,组件的装饰器配置会挂载到类的__annotations__属性上,所以我们能通过这个属性拿到@Component的元数据。
  • 两种方法都不需要依赖TestBed,完全通过手动访问元数据和调用函数来触发代码执行,实现覆盖率统计。

内容的提问来源于stack exchange,提问作者NikZ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 09:12:45