不使用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
相关产品推荐
相关产品推荐

