如何让Jest捕获RxJS Observer内部抛出的异常?
如何让Jest捕获RxJS Observer内部抛出的异常?
我太懂你这种憋屈的感觉了!自己写业务代码的时候,RxJS订阅里根据验证抛的异常能正常捕获,一到Jest测试就掉链子——用done回调吧,直接超时报错;换别的写法又抓不到异常,属实头疼。
其实核心问题是RxJS的错误处理逻辑和Jest的异步测试机制没对齐,我给你几个实用的解决办法,亲测有效:
先改个习惯:别在next回调里硬抛异常,用RxJS的错误通知机制
RxJS本身有一套完善的错误流转体系,你在next里直接抛异常,相当于绕开了这套体系,Jest自然很难捕获到。正确的做法是用操作符(比如tap)来校验并抛出错误,这样RxJS会自动把错误转换成error通知,流也会进入错误状态:
// 不推荐的写法(next里硬抛) someObservable.subscribe({ next: (value) => { if (!isValid(value)) { throw new Error('参数不符合要求!'); } } }); // 推荐的写法(用tap操作符抛错,走RxJS错误流) import { tap } from 'rxjs/operators'; const observableWithValidation = someObservable.pipe( tap(value => { if (!isValid(value)) { throw new Error('参数不符合要求!'); } }) );
测试时的两种靠谱写法
写法1:用async/await + toPromise()(最省心)
把Observable转成Promise,Jest就能像处理普通异步代码一样捕获错误了,还不用管done的问题:
test('应该能抓到RxJS里抛出的异常', async () => { // 先声明要执行1个断言,避免测试提前结束 expect.assertions(1); try { // 把Observable转成Promise await observableWithValidation.toPromise(); } catch (err) { // 断言错误信息符合预期 expect(err.message).toBe('参数不符合要求!'); } });
或者更简洁的,用Jest的rejects匹配器:
test('应该能抓到RxJS里抛出的异常', async () => { expect.assertions(1); // 直接断言Promise会reject,且错误符合预期 return expect(observableWithValidation.toPromise()).rejects.toThrow('参数不符合要求!'); });
写法2:用done回调(适合复杂流场景)
如果你一定要用done,那必须在Observer的error回调里调用done,同时还要处理流正常完成的情况(避免超时):
test('应该能抓到RxJS里抛出的异常', (done) => { expect.assertions(1); observableWithValidation.subscribe({ error: (err) => { expect(err.message).toBe('参数不符合要求!'); // 告诉Jest测试完成 done(); }, complete: () => { // 如果流正常完成了,说明应该抛的异常没抛,测试失败 done.fail('预期会抛出异常,但流正常完成了'); } }); });
为啥你之前用done会超时?就是因为你没在error回调里调用done,Jest一直傻等着,直到超过5秒超时时间。
备注:内容来源于stack exchange,提问作者sartoz
相关产品推荐
相关产品推荐

