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

Angular单元测试:Promise拒绝场景测试报错问题排查

问题:无法覆盖AuthService中SignUp函数的catch代码块

尝试覆盖SignUp函数的catch代码块时遇到两个问题:

  • 版本1:通过spy同步抛出错误,测试直接失败
  • 版本2:让spy返回Promise.reject,能触发catch里的console.log,但断言仍失败

相关代码与报错

AuthService的SignUp函数

SignUp(email: any, password: any, firstName: string, lastName: string) {
  return this.afAuth
    .createUserWithEmailAndPassword(email, password)
    .then((userCredential) => {
      this.SendVerificationMail();
      this.SetUserData(userCredential.user, firstName, lastName);
    })
    .catch((error) => {
      console.log('errorLog: ', error)
      window.alert(error.message);
    });
}

版本1测试代码与报错

测试代码

it('SignUp() - Errors', async () => {
  spyOn(AngularFireAuthMock, 'createUserWithEmailAndPassword').and.callFake(() => { throw new Error('auth/expired-action-code') })
  expect(service.SignUp('test@test.com', '123456', 'Name', 'Two')).toThrowError('auth/expired-action-code');
});

报错信息

Chrome 126.0.0.0 (Windows 10) AuthService SignUp() - Errors FAILED
        Error: auth/expired-action-code
            at Object.<anonymous> (src/app/shared/services/auth.service.spec.ts:58:93)
            at <Jasmine>
            at AuthService.SignUp (src/app/shared/services/auth.service.ts:41:8)
            at UserContext.<anonymous> (src/app/shared/services/auth.service.spec.ts:59:21)
            at Generator.next (<anonymous>)
            at asyncGeneratorStep (node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:3:1)
Chrome 126.0.0.0 (Windows 10): Executed 5 of 5 (1 FAILED) (0.03 secs / 0.016 secs)  

版本2测试代码与报错

测试代码

it('SignUp() - Errors', async () => {
  spyOn(AngularFireAuthMock, 'createUserWithEmailAndPassword').and.callFake(() => Promise.reject(new Error('auth/expired-action-code')))
  expect(service.SignUp('test@test.com', '123456', 'Name', 'Two')).toThrowError('auth/expired-action-code');
});

报错信息

Chrome 126.0.0.0 (Windows 10) AuthService SignUp() - Errors FAILED
        Error: <toThrowError> : Actual is not a Function
        Usage: expect(function() {<expectation>}).toThrowError(<ErrorConstructor>, <message>)
            at <Jasmine>
            at UserContext.<anonymous> (src/app/shared/services/auth.service.spec.ts:59:71)
            at Generator.next (<anonymous>)
            at asyncGeneratorStep (node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:3:1)
            at _next (node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:22:1)
            at executor (node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:27:1)
            at new ZoneAwarePromise (node_modules/zone.js/fesm2015/zone.js:2662:25)
            at UserContext.<anonymous> (node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:19:1)
LOG: 'errorLog: ', Error: auth/expired-action-code                                                                                                  
Error: auth/expired-action-code
    at Object.<anonymous> (http://localhost:9876/_karma_webpack_/webpack:/src/app/shared/services/auth.service.spec.ts:58:100)
    at SpyStrategy.exec (http://localhost:9876/base/node_modules/karma-jasmine/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?86d98e11f43f2c8cfac9fb4806ed6c0aced74cbe:9310:39)

失败原因分析

  1. 版本1问题:createUserWithEmailAndPassword是异步方法,同步抛出错误会直接中断函数执行,而非返回rejected的Promise。SignUp返回的是Promise,同步抛错会绕过内部的catch块直接向外抛出,导致测试未按预期触发catch逻辑。

  2. 版本2问题:SignUp的catch块已经处理了错误,没有重新抛出异常,因此返回的Promise最终是resolved状态,而非rejected。toThrowError仅适用于同步函数抛错或未被处理的Promise rejection,这里用它断言已被处理的异步逻辑自然会失败。

正确测试方案

要覆盖catch块,需断言catch内执行的操作(console.log和window.alert)被正确调用,同时等待Promise完成:

it('SignUp() 应在创建用户失败时触发catch逻辑', async () => {
  const errorMessage = 'auth/expired-action-code';
  // 模拟createUserWithEmailAndPassword返回rejected的Promise
  spyOn(AngularFireAuthMock, 'createUserWithEmailAndPassword')
    .and.returnValue(Promise.reject(new Error(errorMessage)));
  
  // 监听alert和console.log方法
  const alertSpy = spyOn(window, 'alert');
  const consoleLogSpy = spyOn(console, 'log');

  // 等待SignUp的Promise完成
  await service.SignUp('test@test.com', '123456', 'Name', 'Two');

  // 断言alert被调用,参数为错误信息
  expect(alertSpy).toHaveBeenCalledWith(errorMessage);
  // 断言console.log被调用,且错误对象的message符合预期
  expect(consoleLogSpy).toHaveBeenCalledWith('errorLog: ', jasmine.any(Error));
  expect(consoleLogSpy.calls.mostRecent().args[1].message).toBe(errorMessage);
});

关键说明

  • 用returnValue(Promise.reject(...))模拟异步错误,符合原方法的异步特性
  • 等待await service.SignUp(...)确保Promise执行完成,触发catch逻辑
  • 通过监听catch内的副作用(alert、console.log)来验证代码是否走到了catch块,而非断言Promise抛出错误

内容的提问来源于stack exchange,提问作者Rusty Shackleford

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 07:23:15