Angular/Karma测试中如何触发模拟服务的Promise then/catch分支
问题原因
你之前手写的then/catch模拟对象仅做了方法占位,没有执行传入的回调函数,同时测试用例是同步执行的,未等待异步逻辑执行完成就断言,因此拿不到更新后的appMessage值。
解决方案
1. 替换模拟返回值为标准Promise
createUserWithEmailAndPassword本身返回Promise实例,你不需要手动模拟then/catch方法,直接返回原生Promise即可:
- 要触发成功回调
then,返回Promise.resolve() - 要触发失败回调
catch,返回Promise.reject(自定义错误对象)
2. 测试用例增加异步等待逻辑
两种常用写法二选一即可:
写法1:async/await + fixture.whenStable()
// 测试注册成功场景 it('should set success message when sign up succeeds', async () => { spyOn(component, 'signUp').and.callThrough(); // 返回已完成的成功Promise angularFireAuthSignOutSpyObj.createUserWithEmailAndPassword.and.returnValue(Promise.resolve()); component.registrationForm.controls.email.setValue('test@email.com'); component.registrationForm.controls.password.setValue('ValidPass123'); component.registrationForm.controls.passwordCheck.setValue('ValidPass123'); expect(component.registrationForm.valid).toBeTruthy(); debugElement.query(By.css("button")).triggerEventHandler("click", null); expect(component.signUp).toHaveBeenCalled(); expect(component.auth.createUserWithEmailAndPassword) .toHaveBeenCalledWith( component.registrationForm.controls.email.value, component.registrationForm.controls.password.value ); // 等待所有异步任务执行完成 await fixture.whenStable(); // 此时再断言即可拿到正确值 expect(component.appMessage).toEqual('Account created !'); }); // 测试注册失败场景 it('should set error message when sign up fails', async () => { spyOn(component, 'signUp').and.callThrough(); const mockError = { message: '该邮箱已被注册' }; // 返回失败的Promise,触发catch回调 angularFireAuthSignOutSpyObj.createUserWithEmailAndPassword.and.returnValue(Promise.reject(mockError)); component.registrationForm.controls.email.setValue('exist@email.com'); component.registrationForm.controls.password.setValue('ValidPass123'); component.registrationForm.controls.passwordCheck.setValue('ValidPass123'); debugElement.query(By.css("button")).triggerEventHandler("click", null); await fixture.whenStable(); expect(component.appMessage).toEqual(mockError.message); });
写法2:fakeAsync + tick()
如果你偏好同步风格的测试写法,可以用Angular提供的fakeAsync包裹测试用例,调用tick()执行所有微队列任务:
it('should set success message when sign up succeeds', fakeAsync(() => { spyOn(component, 'signUp').and.callThrough(); angularFireAuthSignOutSpyObj.createUserWithEmailAndPassword.and.returnValue(Promise.resolve()); component.registrationForm.controls.email.setValue('test@email.com'); component.registrationForm.controls.password.setValue('ValidPass123'); component.registrationForm.controls.passwordCheck.setValue('ValidPass123'); debugElement.query(By.css("button")).triggerEventHandler("click", null); // 执行所有待处理的微任务 tick(); expect(component.appMessage).toEqual('Account created !'); }));
内容的提问来源于stack exchange,提问作者David Dieppois
相关产品推荐
相关产品推荐

