Angular单元测试报错Expected spy getDateFormat未被调用
问题原因
你遇到的报错核心是两个问题:
- 异步时序问题:
apollo.query()返回的是RxJS Observable,subscribe内部的逻辑是异步执行的。你在调用component.getAllStudents()后立刻执行断言,此时异步请求的回调还没触发,students还没被赋值,forEach循环根本没有运行,自然检测不到getDateFormat和CalculateAge的调用。 - 测试隔离缺失:你对
apollo.query使用了callThrough(),这会发起真实的GraphQL请求,既会让单元测试依赖外部环境导致不稳定,也不符合单元测试隔离依赖的原则。
修复方案
按以下步骤修改测试用例即可:
- 拦截
apollo.query调用,不要发起真实请求,直接返回构造好的mock测试数据 - 用Angular测试工具提供的异步测试能力,等待subscribe内的逻辑执行完成后再跑断言
- 确保mock的学生列表是非空的,且每条数据都带
DateOfBirth字段,否则forEach不会执行,两个工具方法自然不会被调用
修复后参考测试代码
import { of } from 'rxjs'; import { fakeAsync, tick } from '@angular/core/testing'; // 其他测试配置代码省略... describe('getAllStudents', () => { it('makes expected calls', fakeAsync(() => { // 构造mock测试数据 const mockStudentList = [ { DateOfBirth: new Date('2000-02-03') }, { DateOfBirth: new Date('1997-11-12') } ]; // mock apollo.query返回固定测试数据,不发起真实请求 spyOn(apollo, 'query').and.returnValue(of({ data: { getAllStudents: mockStudentList } })); spyOn(component, 'getDateFormat').and.callThrough(); spyOn(component, 'CalculateAge').and.callThrough(); component.getAllStudents(); tick(); // 排空异步任务队列,让subscribe内逻辑同步执行完成 // 断言校验 expect(apollo.query).toHaveBeenCalledWith({ query: GETALL_STUDENTS }); expect(component.CalculateAge).toHaveBeenCalledTimes(2); expect(component.getDateFormat).toHaveBeenCalledTimes(2); // 可额外校验数据处理结果是否符合预期 expect(component.students.length).toBe(2); expect(component.students[0].DateOfBirthString).toBe('2000/02/03'); })); });
额外说明
如果你不想用fakeAsync + tick的组合,也可以用async + fixture.whenStable()的写法处理异步,但是fakeAsync的写法更简洁,调试也更方便。
如果mock返回的getAllStudents是空数组、或者数组项没有有效DateOfBirth字段,forEach不会执行对应逻辑,测试依然会报找不到方法调用的错误,编写用例时需要注意构造的mock数据要覆盖执行路径。
内容的提问来源于stack exchange,提问作者Mad
相关产品推荐
相关产品推荐

