You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 20:15:06