Angular单元测试fixture.whenStable内断言报SPEC HAS NO EXPECTATIONS如何解决
问题原因
- 断言放在
fixture.whenStable().then()回调中时,测试用例的同步代码执行完毕时回调还未触发,Jasmine 检测不到任何expect执行,就会抛出SPEC HAS NO EXPECTATIONS提示,同时断言还没执行测试就已经结束。 - 把断言直接放到外层时,组件的异步变更还未完成,DOM 状态没有更新,所以断言会失败。
解决方案
方案1:使用async/await语法
用JS原生async关键字修饰测试函数,await等待fixture.whenStable()执行完成后再运行断言,Jasmine 可以正常检测到所有断言逻辑:
it('当前在职员工的保存按钮和其余字段应为可编辑状态', async () => { let employee1 = new Employee(); employee1.id = 1; employee1.name = "Vineel pellella"; employee1.workLocation = "Hyderabad"; employee1.email = "vineel.royal@gmail.com"; employee1.contactNo = 9985896040; employee1.dateOfBirth = new Date().toDateString(); employee1.dateOfJoining = new Date().toDateString(); // 若组件判断禁用逻辑依赖在职状态字段,请补充赋值 // employee1.isCurrentEmployed = true; component.emp = employee1; fixture.detectChanges(); // 等待所有异步任务执行完成 await fixture.whenStable(); // 再次执行变更检测,确保DOM状态更新完毕 fixture.detectChanges(); expect(fixture.debugElement.nativeElement.querySelector("#empId").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#empName").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#dateOfBirth").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#designation").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#dateOfJoining").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#workLocation").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#inputGroupSelect01").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#contactNumber").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#email").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#dateOfLeaving").disabled).toBeFalsy(); expect(fixture.debugElement.nativeElement.querySelector("#save").disabled).toBeFalsy(); });
方案2:使用fakeAsync + tick组合
如果测试场景不存在HTTP请求、定时器这类异步宏任务,可以用Angular测试工具提供的fakeAsync包裹测试函数,调用tick()清空所有待执行的微任务后直接写断言即可:
import { fakeAsync, tick } from '@angular/core/testing'; it('当前在职员工的保存按钮和其余字段应为可编辑状态', fakeAsync(() => { let employee1 = new Employee(); employee1.id = 1; employee1.name = "Vineel pellella"; employee1.workLocation = "Hyderabad"; employee1.email = "vineel.royal@gmail.com"; employee1.contactNo = 9985896040; employee1.dateOfBirth = new Date().toDateString(); employee1.dateOfJoining = new Date().toDateString(); component.emp = employee1; fixture.detectChanges(); // 执行所有待处理的微任务 tick(); fixture.detectChanges(); expect(fixture.debugElement.nativeElement.querySelector("#empId").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#empName").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#dateOfBirth").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#designation").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#dateOfJoining").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#workLocation").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#inputGroupSelect01").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#contactNumber").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#email").disabled).toBe(false); expect(fixture.debugElement.nativeElement.querySelector("#dateOfLeaving").disabled).toBeFalsy(); expect(fixture.debugElement.nativeElement.querySelector("#save").disabled).toBeFalsy(); }));
注意事项
- Angular v14及以上版本中,原来的
async测试工具已经重命名为waitForAsync,避免和JS原生async关键字混淆,不建议再使用旧的async()写法。 - 请确认
Employee实例包含组件判断字段禁用逻辑依赖的在职状态字段,如果字段缺失或赋值错误,即便测试执行逻辑正确,断言也会失败。
内容的提问来源于stack exchange,提问作者Vineel Pellella
相关产品推荐
相关产品推荐

