使用Jest测试Angular 1.6服务时遇async callback未调用错误求助
我之前在测试Angular 1.6的服务时也碰到过一模一样的问题——明明服务里没写setTimeout,却总是触发这个异步超时报错。后来发现,罪魁祸首往往是Angular 1.x内置的异步机制(比如$http请求、$q promise),和Jest的异步检测逻辑没匹配上,下面是我总结的几个排查和修复方向:
可能的原因及修复方案
1. 没有手动触发Angular的消化周期
Angular 1.x的异步操作(比如$http返回的promise)依赖**消化周期(digest cycle)**来完成状态更新,但Jest不会自动帮你触发这个周期。哪怕promise已经resolve了,测试也感知不到,最终导致Jest判定异步回调未执行。
修复方法:在测试里手动调用$rootScope.$digest()或者$rootScope.$apply(),同时别忘了用$httpBackend.flush()刷新模拟的HTTP请求:
describe('MarvelService', () => { let MarvelService, $rootScope, $httpBackend; beforeEach(() => { angular.mock.module('yourAppModule'); inject((_MarvelService_, _$rootScope_, _$httpBackend_) => { MarvelService = _MarvelService_; $rootScope = _$rootScope_; $httpBackend = _$httpBackend_; // 模拟Marvel API的响应 $httpBackend.whenGET(/https:\/\/gateway.marvel.com\/.*$/).respond(200, { data: [] }); }); }); it('should fetch characters successfully', (done) => { MarvelService.getCharacters().then((response) => { expect(response.data).toEqual([]); done(); // 告诉Jest异步操作已完成 }); $httpBackend.flush(); // 触发模拟请求的响应 $rootScope.$digest(); // 启动Angular消化周期 }); });
2. 测试函数未传递异步结束信号给Jest
Jest需要明确知道异步测试什么时候结束,如果你的测试包含异步逻辑(比如服务里的$q promise),要么在回调里调用done(),要么让测试函数直接返回一个promise。
比如用返回promise的简化写法:
it('should fetch characters successfully', () => { const promise = MarvelService.getCharacters(); $httpBackend.flush(); $rootScope.$digest(); return promise.then((response) => { expect(response.data).toEqual([]); }); });
3. 未正确模拟$http请求(若服务依赖HTTP调用)
如果你的Marvel Service直接调用了真实的Marvel API,网络请求的延迟会让Jest超时。必须用$httpBackend模拟所有HTTP请求,确保请求能被立即响应。
上面示例中的$httpBackend.whenGET(...).respond(...)和$httpBackend.flush()就是关键步骤,不能省略。
4. Jest默认超时时间太短
虽然这种情况比较少见,但如果你的测试逻辑确实需要更长时间执行,可以手动延长超时时间:
it('should fetch characters successfully', (done) => { // ...测试逻辑 done(); }, 10000); // 设置10秒超时(默认是5秒)
总结
绝大多数情况下,这个问题都是因为Angular消化周期未触发或者Jest没收到异步结束的信号。按照上面的步骤逐一排查,应该能快速解决问题。
内容的提问来源于stack exchange,提问作者Lucas_Santos

