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

如何使用$provide和$q对依赖Promise工厂的服务进行单元测试?

解决Angular单元测试中Promise then()代码未执行的问题

我之前在写Angular单元测试的时候也碰到过一模一样的问题——明明逻辑没问题,但then()里的代码就是不跑,后来发现核心原因是依赖返回的Promise一直处于pending状态没被触发,再加上Angular的消化循环没执行,导致回调永远不会触发。不用spyOn完全可以解决,给你一步步拆解方案:

1. 手动模拟依赖的Promise返回值

首先,咱们要给managerService依赖的managerModel手动返回一个已完成状态的Promise(要测错误分支就返回rejected的)。在测试的beforeEach块里,用$provide来替换掉真实的managerModel:

beforeEach(module('test', ($provide) => {
  // 模拟managerModel服务,让它的方法返回立即resolve的Promise
  $provide.factory('managerModel', () => {
    return {
      // 这里替换成你实际调用的方法名,带上测试需要的mock数据
      fetchData: () => Promise.resolve({ id: 1, name: 'test' })
    };
  });
}));

2. 触发Angular的消化循环

Angular的变更检测需要靠digest循环驱动,哪怕你用的是原生Promise,只要是在Angular上下文里,调用$rootScope.$digest()或者$rootScope.$apply()才能让Promise的回调执行。这是很多人容易漏掉的关键步骤:

it('should execute the code inside then()', inject((managerService, $rootScope) => {
  let isExecuted = false;

  // 调用服务里的方法
  managerService.processData().then(() => {
    isExecuted = true; // 你要验证的then里的逻辑
  });

  // 触发消化循环,让Promise回调执行
  $rootScope.$digest();

  // 现在可以断言逻辑是否执行了
  expect(isExecuted).toBe(true);
}));

3. 完整测试示例(贴合你的代码结构)

假设你的managerService大概是这个样子:

app.factory('managerService', (managerModel) => {
  const service = {};

  service.processData = () => {
    return managerModel.fetchData().then(data => {
      // 这就是你要测试的then内的代码
      return data.name.toUpperCase();
    });
  };

  return service;
});

对应的测试代码可以这么写:

describe('managerService', () => {
  beforeEach(module('test', ($provide) => {
    // 替换真实的managerModel为模拟版本
    $provide.factory('managerModel', () => {
      return {
        fetchData: () => Promise.resolve({ name: 'angular' })
      };
    });
  }));

  it('should transform data in then()', inject((managerService, $rootScope) => {
    let processedResult;

    managerService.processData().then(result => {
      processedResult = result;
    });

    // 必须触发digest,否则回调不会执行
    $rootScope.$digest();

    expect(processedResult).toBe('ANGULAR');
  }));
});

几个容易踩的坑

  • 如果你用的是Angular自带的$q而不是原生Promise,记得用$q.resolve()来创建Promise,同样需要触发digest循环。
  • 千万别忘记调用$digest()/$apply()——这是让Promise回调“生效”的关键,没这一步,代码永远挂在pending状态。
  • 要测试错误分支的话,只需要把模拟的Promise改成Promise.reject(new Error('test error')),然后用.catch()来验证错误处理逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:36:38