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

Jasmine问题:AngularJS服务单元测试中异步调用无法获取响应

解决AngularJS Jasmine异步服务测试无法获取响应的问题

看起来你在测试AngularJS服务的异步方法时踩了几个典型的异步测试陷阱,我来帮你一步步排查和修复:

你的测试代码里的关键问题

  • 重复调用done():在then、catch、finally里都调用了done(),这会导致测试在第一个回调触发时就提前结束,哪怕then里的断言还没执行完成。比如promise被resolve后,then和finally都会触发,两次调用done()会导致测试报错或者结果不可靠。
  • $rootScope.$apply()的时机可能不对:Angular的promise依赖digest循环来触发回调,如果你是手动模拟promise的resolve,需要在resolve之后调用$apply();如果用$httpBackend,则需要调用$httpBackend.flush()来触发响应。
  • 缺少对依赖服务的模拟:如果getData()是通过$http或者其他异步服务获取数据,测试时必须模拟这些依赖的响应,否则真实的异步请求不会在测试环境中完成。

修正后的测试用例示例

假设你的service.getData()依赖$http,我们用$httpBackend来模拟响应,同时修复done()的问题:

describe('#getData', function () {
  // 注入测试所需的服务
  beforeEach(inject(function (_service_, _$rootScope_, _$httpBackend_) {
    service = _service_;
    $rootScope = _$rootScope_;
    $httpBackend = _$httpBackend_;
    
    // 模拟getData对应的HTTP请求
    $httpBackend.whenGET('/api/data').respond(mockData);
  }));

  afterEach(function () {
    // 确保所有模拟的请求都被处理
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();
  });

  it('Gets the data', function (done) {
    var promise = service.getData();
    
    // 触发模拟的HTTP响应
    $httpBackend.flush();
    
    promise.then(function (resp) {
      expect(resp).toEqual(mockData);
      done(); // 只在成功断言后调用done
    }).catch(function (error) {
      // 如果进入catch,测试应该失败,用fail()标记后再结束
      fail('getData promise rejected unexpectedly: ' + error);
      done();
    });
  });
});

关键修复点说明

  • 移除重复的done()调用:只在断言完成后调用一次done(),catch分支触发时说明测试失败,用fail()标记后再调用done()结束测试。
  • 使用$httpBackend模拟异步请求:对于依赖$http的服务,必须用$httpBackend拦截并响应请求,再调用flush()同步触发响应,这样promise会立即resolve。
  • 手动触发digest循环(针对自定义promise):如果你的getData()是用$q手动创建的promise,比如:
    this.getData = function() {
      var deferred = $q.defer();
      // 模拟异步操作
      setTimeout(function() {
        deferred.resolve(mockData);
      }, 0);
      return deferred.promise;
    };
    
    那测试时需要在调用getData()后,手动调用$rootScope.$apply()来触发promise的回调:
    it('Gets the data', function (done) {
      var promise = service.getData();
      $rootScope.$apply(); // 触发digest循环,让promise回调执行
      
      promise.then(function (resp) {
        expect(resp).toEqual(mockData);
        done();
      }).catch(function () {
        fail('Promise rejected unexpectedly');
        done();
      });
    });
    

额外简化技巧

如果你用的是Jasmine 2.x及以上版本,其实可以不用done(),直接返回promise来处理异步测试,代码会更简洁:

it('Gets the data', function () {
  var promise = service.getData();
  $httpBackend.flush();
  
  return promise.then(function (resp) {
    expect(resp).toEqual(mockData);
  });
});

Jasmine会自动等待promise完成,如果promise被reject,测试会自动标记为失败。

内容的提问来源于stack exchange,提问作者31piy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:22:47