Angular单元测试Promise、数组方法、Observable覆盖问题咨询
问题1:confirmationDialogService.confirm返回的Promise.then逻辑无法覆盖
首先你需要手动mock该服务的confirm方法,持有Promise的resolve控制权,等待异步队列清空后再断言:
- 测试初始化阶段构造mock服务,暴露resolve方法
let mockConfirmResolve: (confirmResult: boolean) => void; beforeEach(() => { const mockDialogService = jasmine.createSpyObj('ConfirmationDialogService', ['confirm']); mockDialogService.confirm.and.returnValue(new Promise(resolve => { mockConfirmResolve = resolve; })); TestBed.configureTestingModule({ providers: [{ provide: ConfirmationDialogService, useValue: mockDialogService }] }); });
- 编写用例时手动触发confirm结果,等待Promise执行完成
it('should execute then logic when confirm is accepted', async () => { // 触发组件内调用confirm的逻辑 component.triggerConfirmAction(); // 模拟用户点击确认/取消 mockConfirmResolve(true); // 等待所有Promise回调执行完成 await fixture.whenStable(); // 断言then内的逻辑执行结果 expect(component.targetMethod).toHaveBeenCalled(); });
如果偏好同步测试写法,也可以用fakeAsync+tick替代async/await+whenStable。
问题2:包含forEach/filter/map等数组方法的函数测试
不需要单独测试JS原生数组方法的正确性,只需要针对你的业务逻辑构造边界输入,断言输出/副作用符合预期即可:
比如针对过滤+转换产品列表的方法:
// 组件内方法示例 getValidProducts(products: Product[]) { return products .filter(p => p.stock > 0 && p.status === 'online') .map(p => ({ ...p, tag: '可售' })); }
测试用例写法:
it('should return only valid products with sale tag', () => { // 构造覆盖所有分支的测试输入 const testInput = [ { id: 1, stock: 10, status: 'online', name: '产品1' }, { id: 2, stock: 0, status: 'online', name: '产品2' }, { id: 3, stock: 5, status: 'offline', name: '产品3' } ]; const result = component.getValidProducts(testInput); // 断言结果符合业务预期 expect(result.length).toBe(1); expect(result[0].id).toBe(1); expect(result[0].tag).toBe('可售'); });
如果是带副作用的forEach逻辑,直接断言输入参数的变化或者依赖方法的调用次数即可。
问题3:包含Observable、subscribe、Promise的方法测试最佳实践
3.1 Observable+subscribe测试
普通业务场景下用fakeAsync+tick即可覆盖,复杂的流操作可以用jasmine-marbles做弹珠测试:
it('should update list after load api returns', fakeAsync(() => { const mockList = [{ id: 1, name: '测试产品' }]; // mock service返回Observable (productService.getList as jasmine.Spy).and.returnValue(of(mockList)); // 触发组件加载逻辑 component.loadList(); // 等待Observable回调执行 tick(); // 断言subscribe内逻辑执行结果 expect(component.productList).toEqual(mockList); }));
如果要测试error分支,把mock返回值替换为throwError(() => new Error('请求失败'))即可。
3.2 混合Promise和Observable的场景
如果同一个方法内同时存在两种异步逻辑,优先用async/await+fixture.whenStable()的组合,会自动等待所有异步队列清空,不需要单独处理不同的异步类型。
通用最佳实践
- 所有外部依赖(服务、弹窗、第三方库)都做mock,单元测试只覆盖当前组件的业务逻辑,不要引入真实依赖实现
- 异步逻辑的断言必须放在异步等待语句之后,禁止用同步断言判断异步逻辑的执行结果
- 单个测试用例只覆盖一个逻辑分支,不要把多个场景揉在同一个
it块内 - 测试用例描述要明确对应业务场景,比如
should clear form when cancel button clicked比test cancel function可读性更强
内容的提问来源于stack exchange,提问作者aparna bhargav
相关产品推荐
相关产品推荐

