如何使用$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
相关产品推荐
相关产品推荐

