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问题:
createUserWithEmailAndPassword是异步方法,同步抛出错误会直接中断函数执行,而非返回rejected的Promise。SignUp返回的是Promise,同步抛错会绕过内部的catch块直接向外抛出,导致测试未按预期触发catch逻辑。版本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
相关产品推荐
相关产品推荐

