Angular13设destroyAfterEach仍报Injector已销毁如何排查作用域泄漏
问题报错
触发的核心报错信息:Unhandled promise rejection: Error: Injector has already been destroyed。
官方给出的参考修复方案
在近期已关闭的Angular官方issue中,贡献者@jessicajaniuk在评论中给出官方结论:
配置
destroyAfterEach: true即可修复该问题;如果必须设置destroyAfterEach: false才能避免测试失败,大概率是测试代码存在作用域泄漏(scope leakage),若原问题仍复现可提交新issue反馈。
当前未解决场景
升级至Angular 13、且已配置destroyAfterEach: true的场景下仍然触发该报错,需要可落地的测试代码作用域泄漏识别方法、排查策略。
可落地的排查方向
- 优先排查未被清理的异步任务
这类问题占这类报错的80%以上:测试执行结束后Injector已经被销毁,但之前挂载的异步回调还在执行,回调中尝试访问注入器内的服务/实例就会触发报错。重点检查:- 所有
setTimeout、setInterval是否在afterEach钩子中对应执行了clearTimeout、clearInterval - 所有RxJS订阅是否在测试结束时调用了
unsubscribe,或者通过takeUntil操作符配合测试销毁信号完成自动取消 - 全局事件监听、路由事件监听、宿主元素事件监听是否在测试结束时移除
- 长连接、WebSocket等异步资源是否在测试结束时主动断开
- 所有
- 排查跨用例的实例污染问题
检查是否存在将TestBed.inject获取的服务实例、组件实例、Fixture实例缓存在describe块作用域、甚至全局作用域的写法,尤其是加了非空判断、只在第一次初始化时赋值的逻辑,这类写法会导致后续用例拿到已经被销毁的旧实例。
错误写法示例:
这类问题直接去掉实例缓存判断,每次describe('TestSuite', () => { let testService: TestService; beforeEach(() => { TestBed.configureTestingModule({/* 测试模块配置 */}); // 错误逻辑:第一次赋值后后续用例不会重新获取实例,会持有已销毁的旧引用 if (!testService) { testService = TestBed.inject(TestService); } }); });beforeEach执行时重新从当前TestBed获取实例即可修复。 - 排查测试模块重置时机错误
检查是否存在手动调用TestBed.resetTestingModule()的逻辑,确认调用时机在所有异步操作执行完成之后,避免异步回调还在队列中就提前销毁了注入器。 - 排查Fixture异步等待遗漏
检查所有用到fixture.whenStable()、fixture.whenRenderingDone()的用例,是否存在没有等待异步渲染完成就直接结束用例的情况,这类问题会导致渲染完成回调在测试销毁后才触发。
快速定位小技巧:在报错抛出的位置加断点,查看调用栈定位是哪个异步回调触发的报错,对应找到所属的组件/服务、所属测试用例即可快速锁定泄漏点。
内容的提问来源于stack exchange,提问作者skzryzg
相关产品推荐
相关产品推荐

