Jasmine测试无返回值异步函数断言提前执行的解决方法
Jasmine Angular组件异步方法单元测试方案
问题说明
被测方法为业务组件内部的getPolicyUrl,逻辑为调用服务方法获取数据后异步赋值组件属性,原方法代码:
getPolicyUrl() { this.insuranceService.getData(this.endpointURL).subscribe((response) => { this.insuranceTermsUrl = response.PolicyUrl; }); }
原有测试代码执行时抛出错误:Error: Expected '' to equal 'https://google.com'.,根因为Observable订阅回调为异步微任务,断言执行时赋值逻辑尚未触发。要求不修改原被测函数、不为原函数新增返回值。
失败原因
之前尝试的spyOn、async/await、done()方案未生效核心原因:
- spyOn打桩对象错误:未使用TestBed注入的、与组件内实例一致的InsuranceService打桩,导致打桩不生效,实际仍执行原服务的异步逻辑
- async/await无法监听Observable订阅回调的执行时机,不会等待回调执行完成
- done()需要在回调逻辑执行完成后主动调用,不修改原函数的前提下无法正确触发
可行实现(无需修改原业务代码)
使用Angular测试工具集提供的fakeAsync+tick控制异步时序,配合正确的服务打桩即可解决问题,完整测试代码如下:
// 先引入所需测试工具 import { fakeAsync, tick, TestBed } from '@angular/core/testing'; import { of } from 'rxjs'; import { InsuranceService } from '你的服务实际路径'; import { TermsDetailsComponent } from '你的组件实际路径'; beforeEach(() => { fixture = TestBed.createComponent(TermsDetailsComponent); component = fixture.componentInstance; }); it('should create', () => { expect(component).toBeTruthy(); }); it('should change the value of terms url variable', fakeAsync(() => { // 获取测试注入器中与组件绑定的同一个服务实例 const insuranceService = TestBed.inject(InsuranceService); // 对服务方法打桩,返回携带预设测试值的同步Observable spyOn(insuranceService, 'getData').and.returnValue(of({ PolicyUrl: 'https://google.com' })); component.insuranceTermsUrl = ''; component.getPolicyUrl(); // 清空所有待执行微任务,强制执行subscribe内的赋值逻辑 tick(); // 赋值完成后执行断言 expect(component.insuranceTermsUrl).toEqual('https://google.com'); }));
替代方案(waitForAsync写法)
如果不使用fakeAsync,也可以用waitForAsync+fixture.whenStable()等待异步队列清空后断言:
import { waitForAsync } from '@angular/core/testing'; it('should change the value of terms url variable', waitForAsync(() => { const insuranceService = TestBed.inject(InsuranceService); spyOn(insuranceService, 'getData').and.returnValue(of({ PolicyUrl: 'https://google.com' })); component.insuranceTermsUrl = ''; component.getPolicyUrl(); fixture.whenStable().then(() => { expect(component.insuranceTermsUrl).toEqual('https://google.com'); }); }));
两种方案都不需要修改原有业务代码,优先推荐fakeAsync写法,时序更可控,代码可读性更高。
内容的提问来源于stack exchange,提问作者programmer24
相关产品推荐
相关产品推荐

