AngularJS/Jasmine如何测试同一函数带不同参数的多次调用?
这里有一套针对你这个递归重试场景的单元测试方案,结合AngularJS常用的Jasmine测试框架来实现,思路和代码示例如下:
测试核心思路
- 首先得把
customService的方法给Mock掉,这样我们能完全控制它的返回值,模拟出第一次返回409错误、第二次调用成功的场景。 - 重点测试两种核心情况:第一次调用就顺利成功的流程,以及触发409错误后修改参数、递归重试并最终成功的流程。
- 因为涉及Promise和递归调用,要注意处理Angular的Digest循环,确保异步逻辑能在测试中正确执行,同时还要验证参数确实被修改后再重试。
具体测试实现(Jasmine为例)
先在测试套件里准备好依赖和Mock对象:
describe('testSomething function', function() { var $q, $rootScope, customService; beforeEach(module('yourAppModule')); // 替换成你的Angular模块名 beforeEach(inject(function(_$q_, _$rootScope_) { $q = _$q_; $rootScope = _$rootScope_; // 创建customService的Mock对象,模拟两个方法 customService = jasmine.createSpyObj('customService', ['doSomething', 'doSomethingElse']); // 这里要确保testSomething能拿到这个Mock对象,比如通过依赖注入或者直接替换原服务 })); // 测试场景1:第一次调用就成功 it('should return the result when first call succeeds', function() { // 模拟两个服务方法都返回成功Promise const mockSuccessResult = 'test success data'; customService.doSomething.and.returnValue($q.resolve()); customService.doSomethingElse.and.returnValue($q.resolve(mockSuccessResult)); const initialStuff = { id: 1 }; const resultPromise = testSomething(initialStuff); // 触发Angular的Digest循环,让Promise执行完成 $rootScope.$digest(); // 验证服务方法的调用次数 expect(customService.doSomething).toHaveBeenCalledTimes(1); expect(customService.doSomethingElse).toHaveBeenCalledTimes(1); // 验证返回结果正确 resultPromise.then(function(result) { expect(result).toBe(mockSuccessResult); }); }); // 测试场景2:第一次返回409,修改参数后重试成功 it('should retry with modified stuff when 409 error occurs', function() { // 模拟第一次调用doSomething返回409错误,第二次返回成功 const firstErrorResponse = { status: 409 }; customService.doSomething.and.callFake(function() { if (customService.doSomething.calls.count() === 1) { return $q.reject(firstErrorResponse); } else { return $q.resolve(); } }); // 模拟doSomethingElse在重试时返回成功结果 const retrySuccessResult = 'retry success data'; customService.doSomethingElse.and.returnValue($q.resolve(retrySuccessResult)); const initialStuff = { id: 1 }; // 假设你的代码里会把stuff修改成这个样子,根据实际逻辑调整 const modifiedStuff = { id: 2 }; // Spy目标函数,跟踪它的调用参数和次数 spyOn(window, 'testSomething').and.callThrough(); // 如果函数在服务里,替换成对应的服务对象 const resultPromise = testSomething(initialStuff); // 第一次Digest处理第一次的错误,触发重试逻辑 $rootScope.$digest(); // 第二次Digest处理重试后的成功流程 $rootScope.$digest(); // 验证函数被调用两次,参数正确更新 expect(testSomething).toHaveBeenCalledTimes(2); expect(testSomething.calls.argsFor(0)).toEqual([initialStuff]); expect(testSomething.calls.argsFor(1)).toEqual([modifiedStuff]); // 验证服务方法的调用次数 expect(customService.doSomething).toHaveBeenCalledTimes(2); expect(customService.doSomethingElse).toHaveBeenCalledTimes(1); // 只有第二次调用才走到这一步 // 验证最终返回的结果是重试后的成功数据 resultPromise.then(function(result) { expect(result).toBe(retrySuccessResult); }); }); });
关键注意事项
- Promise的Digest循环:Angular的
$q依赖Digest循环来触发Promise的回调,所以每次异步操作后都要调用$rootScope.$digest(),否则测试会看不到异步结果。 - 递归Promise的链式处理:你的原代码里递归调用
testSomething(stuff)时没有返回这个Promise,会导致外层Promise无法跟踪重试后的结果。建议修改原代码的错误处理部分:function(error) { if(error.status === 409) { // 修改参数逻辑 stuff = newStuff; return testSomething(stuff); // 加上return,让Promise链完整延续 } } - Mock的精准控制:用
callFake来根据调用次数返回不同结果,能精准模拟第一次失败、第二次成功的场景,确保测试覆盖到重试逻辑。
内容的提问来源于stack exchange,提问作者Hrishikesh Sardar
相关产品推荐
相关产品推荐

