如何在不使用setTimeout的情况下测试Angular异步函数?
解决Angular异步函数测试中setTimeout的问题
我太懂这种测试里被setTimeout搞疯的感觉了——明明逻辑没问题,却时不时因为超时不稳定,还得等半天才能跑完测试。咱们分两种场景来解决:
场景1:可以修改服务代码(最优方案)
Angular本身提供了$timeout服务,它是原生setTimeout的封装,完美适配Angular的消化循环(digest cycle),测试时可以手动触发回调,完全不需要等实际时间。
修改后的服务代码:
angular.module('moduloPrueba', []) .factory('asincronico', function($q, $timeout) { // 注入$timeout return { tes: tes, }; function tes(){ var deferred = $q.defer(); // 把原生setTimeout换成$timeout $timeout(function () { deferred.resolve(79); }, 500); // 这里填你原来的延迟时间 return deferred.promise; } });
对应的测试代码(Jasmine):
describe('asincronico service', function() { var asincronico, $timeout, $q; // 加载模块 beforeEach(module('moduloPrueba')); // 注入需要的服务 beforeEach(inject(function(_asincronico_, _$timeout_, _$q_) { asincronico = _asincronico_; $timeout = _$timeout_; $q = _$q_; })); it('should resolve to 79 without real delay', function() { var result; // 调用异步函数 asincronico.tes().then(function(val) { result = val; }); // 关键:手动触发所有$timeout的回调,不用等实际时间 $timeout.flush(); // 验证结果 expect(result).toBe(79); }); });
场景2:无法修改服务代码(比如第三方服务)
如果不能改原服务的setTimeout,咱们可以mock原生的setTimeout,让它直接执行回调,再手动触发Angular的消化循环让promise生效。
测试代码示例:
describe('asincronico service (unmodified)', function() { var asincronico, $q; var originalSetTimeout; beforeEach(module('moduloPrueba')); beforeEach(inject(function(_asincronico_, _$q_) { asincronico = _asincronico_; $q = _$q_; // 先保存原生setTimeout,测试完要恢复,避免影响其他测试 originalSetTimeout = window.setTimeout; // mock setTimeout:直接传入回调就执行,不等待 window.setTimeout = function(callback) { callback(); }; })); // 测试后恢复原生setTimeout afterEach(function() { window.setTimeout = originalSetTimeout; }); it('should resolve to 79 immediately', function() { var result; asincronico.tes().then(function(val) { result = val; }); // 关键:触发Angular的消化循环,让promise的回调执行 $q.flush(); expect(result).toBe(79); }); });
为什么之前用jasmine-promises没用?
因为原生setTimeout脱离了Angular的消化循环,即使promise已经resolve了,Angular也不会自动更新视图或执行then回调,必须手动调用$q.flush()或者用Angular自己的$timeout来触发消化循环。
内容的提问来源于stack exchange,提问作者Ivan Paredes
相关产品推荐
相关产品推荐

