如何在Jasmine中对throw new Error()代码段编写单元测试
问题根源
你遇到的两个问题本质都是对RxJS异步逻辑的测试写法不正确,还有部分语法错误:
- 外层
try/catch无法捕获异步错误:subscribe的错误回调是异步执行的,调用component.addNew(4)时只是完成了订阅动作,错误回调还未触发,try块就已经执行完毕,catch分支永远不会进入,内部的expect断言从未执行,所以无论填什么内容测试都会显示通过。 - 未处理的异步错误直接输出控制台:异步抛出的Error不在当前同步执行的上下文里,没有被你的测试代码捕获,Jasmine就会把这个未处理错误直接打印到控制台。
- 额外的语法错误:你原来的测试中
catch分支用到的item变量未声明,就算catch能触发也会先报变量未定义的错误,同时expect的断言对象也写错了:orderMockService.add本身不会抛出错误,抛出错误的是组件内的错误回调逻辑。
修复方案
提供两种常用的修复写法,你可以根据自己的场景选择:
写法1:用done回调捕获全局异步错误
适合不需要修改组件代码的场景,直接重写全局错误拦截逻辑捕获抛出的错误:
import { throwError } from 'rxjs'; // 其余测试初始化逻辑保持不变,比如组件声明、mock service注入等 it('should throw custom error when service add fails', (done) => { const testItem = 4; const expectedErrorMsg = `Item no ${testItem} was not added to the DB`; // mock service返回错误 orderMockService.add.and.returnValue(throwError(() => ({ status: 500 }))); // 重写全局错误拦截,捕获异步抛出的错误 spyOn(window, 'onerror').and.callFake((errorMsg) => { // 断言错误消息符合预期 expect(errorMsg.toString()).toContain(expectedErrorMsg); // 断言service方法被正确调用 expect(orderMockService.add).toHaveBeenCalledOnceWith(testItem); // 通知Jasmine异步测试执行结束 done(); // 阻止错误继续输出到控制台 return true; }); component.addNew(testItem); });
写法2:用Angular测试工具fakeAsync+tick
更贴合Angular单测的常用写法,不需要修改全局事件:
import { throwError } from 'rxjs'; import { fakeAsync, tick } from '@angular/core/testing'; it('should throw custom error when service add fails', fakeAsync(() => { const testItem = 4; const expectedErrorMsg = `Item no ${testItem} was not added to the DB`; orderMockService.add.and.returnValue(throwError(() => ({ status: 500 }))); let caughtError: Error | null = null; // 拦截控制台错误捕获抛出的异常 spyOn(console, 'error').and.callFake((err) => { if (err instanceof Error) caughtError = err; }); component.addNew(testItem); tick(); // 手动触发所有异步任务执行完毕 expect(orderMockService.add).toHaveBeenCalledOnceWith(testItem); expect(caughtError?.message).toBe(expectedErrorMsg); }));
可选优化(推荐)
如果允许调整组件代码,建议把错误处理逻辑抽成独立方法,测试会更简单,也不需要处理异步捕获的问题:
调整后的组件代码
addNew(item: number) { this.service.add(item).subscribe( res => { this.sendNoti(item); }, err => { this.handleAddItemError(item); } ) } // 抽离独立的错误处理方法 handleAddItemError(item: number) { throw new Error(`Item no ${item} was not added to the DB`); }
对应的测试代码
import { throwError } from 'rxjs'; import { fakeAsync, tick } from '@angular/core/testing'; it('should trigger error handler when service add fails', fakeAsync(() => { const testItem = 4; // 监听错误处理方法的调用 spyOn(component, 'handleAddItemError'); orderMockService.add.and.returnValue(throwError(() => ({ status: 500 }))); component.addNew(testItem); tick(); // 直接断言方法被正确调用即可,不需要捕获异步错误 expect(component.handleAddItemError).toHaveBeenCalledOnceWith(testItem); })); // 单独测试错误处理方法的抛出逻辑,同步测试非常简单 it('handleAddItemError should throw correct error message', () => { expect(() => component.handleAddItemError(4)).toThrowError('Item no 4 was not added to the DB'); });
内容的提问来源于stack exchange,提问作者morrigannn
相关产品推荐
相关产品推荐

