You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:49:44