如何用Jest无TestBed测试含inject()的Angular组件/服务?
解决Angular中使用inject()的类的纯Jest测试问题
核心问题分析
当类通过字段初始化器直接调用inject()时(比如protected destroyRef: DestroyRef = inject(DestroyRef);),Angular强制要求该调用必须在注入上下文内执行。纯JavaScript测试环境没有Angular DI上下文支撑,直接new实例就会触发NG0203错误。
方案1:模拟inject()方法(最贴近纯JS测试)
用Jest直接mock@angular/core中的inject函数,让它返回你需要的模拟对象,彻底绕开Angular DI机制:
// 测试文件顶部先mock依赖 jest.mock('@angular/core', () => ({ ...jest.requireActual('@angular/core'), // 保留Angular其他原生导出 inject: jest.fn((token: any) => { // 根据注入的token返回对应模拟对象 if (token.name === 'DestroyRef') { return { onDestroy: jest.fn() // 模拟DestroyRef的核心方法 }; } // 其他需要注入的token可在此扩展 throw new Error(`未mock的token: ${token}`); }) })); describe('ValueService', () => { let service: ValueService; beforeEach(() => { // 每次测试前重置mock状态 (jest.mocked(require('@angular/core').inject) as jest.Mock).mockClear(); service = new ValueService(); }); test('#getValue 应返回正确值', () => { expect(service.getValue()).toBe('real value'); // 还能验证inject是否被正确调用 expect(require('@angular/core').inject).toHaveBeenCalledWith(DestroyRef); }); });
这个方案完全脱离Angular上下文,纯JS环境即可运行,完美匹配你“无Angular上下文测试”的需求。
方案2:用runInInjectionContext创建最小注入上下文
如果不想完全mockinject,可以用Injector.create创建最小化注入器,配合runInInjectionContext提供合法的注入环境,同时注册所需的模拟提供者:
import { runInInjectionContext, Injector, DestroyRef } from '@angular/core'; describe('ValueService', () => { let service: ValueService; let mockDestroyRef: Partial<DestroyRef>; beforeEach(() => { mockDestroyRef = { onDestroy: jest.fn() }; // 创建包含模拟提供者的注入器 const injector = Injector.create({ providers: [ { provide: DestroyRef, useValue: mockDestroyRef } ] }); // 在注入上下文内实例化服务 runInInjectionContext(injector, () => { service = new ValueService(); }); }); test('#getValue 应返回正确值', () => { expect(service.getValue()).toBe('real value'); // 验证DestroyRef的方法是否能被正常调用 service.someMethodThatUsesOnDestroy(); expect(mockDestroyRef.onDestroy).toHaveBeenCalled(); }); });
注意:Injector.create必须明确注册所有被inject()请求的token,不能用any模糊匹配——这也是你之前尝试失败的核心原因。
总结
- 追求纯JS无Angular上下文:选方案1,直接mock
inject函数 - 需要保留部分Angular DI逻辑:选方案2,用最小注入器提供合法上下文
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

