如何测试含async/await异步函数的组件?遇TypeError报错求助
解决TypeError: Cannot read properties of undefined (reading 'loadTodos')问题
问题根源
报错核心是组件内的testService实例未正确初始化,测试环境没给组件注入该服务的模拟实例,导致调用this.testService.loadTodos()时this.testService为undefined。另外你的测试用例还存在异步处理漏洞:直接调用component.loadTodos()后立即断言,会因为异步操作未完成导致断言失败。
分步解决方案
1. 正确创建并注入TestService的模拟Spy
在测试模块的TestBed配置中,先创建服务的spy对象,再将其作为依赖提供给组件:
// 测试文件顶部的配置部分 describe('YourComponent', () => { let component: YourComponent; let fixture: ComponentFixture<YourComponent>; let testServiceSpy: jasmine.SpyObj<TestService>; beforeEach(async () => { // 创建TestService的spy,指定要模拟的方法 testServiceSpy = jasmine.createSpyObj('TestService', ['loadTodos']); await TestBed.configureTestingModule({ declarations: [YourComponent], // 提供模拟的TestService providers: [ { provide: TestService, useValue: testServiceSpy } ], // 若组件用到HttpClient,必须导入测试模块 imports: [HttpClientTestingModule] }) .compileComponents(); fixture = TestBed.createComponent(YourComponent); component = fixture.componentInstance; // 此时组件的testService已被替换为我们的spy实例 }); });
2. 修正测试用例的异步处理
因为loadTodos是异步函数,测试时必须等待其执行完成再断言,以下是两种常用实现方式:
方式一:使用async/await
it('should load todos correctly', async () => { const response2 = [ { userId: 1, id: 1, title: 'delectus aut autem', completed: false, }, { userId: 1, id: 2, title: 'quis ut nam facilis et officia qui', completed: false, }, ]; // 让spy返回一个已resolved的Promise testServiceSpy.loadTodos.and.resolveTo(response2); // 等待异步函数执行完成 await component.loadTodos(); expect(component.todos).toEqual(response2); // 可选:验证spy是否被调用过 expect(testServiceSpy.loadTodos).toHaveBeenCalled(); });
方式二:使用fakeAsync/tick
it('should load todos correctly', fakeAsync(() => { const response2 = [ { userId: 1, id: 1, title: 'delectus aut autem', completed: false, }, { userId: 1, id: 2, title: 'quis ut nam facilis et officia qui', completed: false, }, ]; testServiceSpy.loadTodos.and.returnValue(Promise.resolve(response2)); component.loadTodos(); // 快进所有异步操作,模拟时间流逝 tick(); expect(component.todos).toEqual(response2); expect(testServiceSpy.loadTodos).toHaveBeenCalled(); }));
额外注意点
- 若使用Jest而非Jasmine,创建spy的方式为
const testServiceSpy = jest.mocked(jest.fn(() => ({ loadTodos: jest.fn() })));,核心思路一致:确保组件能获取到服务的模拟实例。 - 服务中使用的
toPromise()已被Angular弃用,建议改用lastValueFrom或firstValueFrom,但这不是当前报错的直接原因。
内容的提问来源于stack exchange,提问作者Ricardo Tovar
相关产品推荐
相关产品推荐

