使用Jasmine Marbles测试RxJS catch操作符始终失败
我来帮你搞定这个测试失败的问题,其实根源在于对RxJS流行为和jasmine-marbles的marble语法理解有一点小偏差。
问题分析
先看测试失败的输出:
Expected [ Object({ frame: 10, notification: Notification({ kind: 'N', value: 0, error: undefined, hasValue: true }) }), Object({ frame: 10, notification: Notification({ kind: 'C', value: undefined, error: undefined, hasValue: false }) }) ] to equal [ Object({ frame: 10, notification: Notification({ kind: 'N', value: 0, error: undefined, hasValue: true }) }) ].
这段输出的意思很明确:
- 实际的Observable流在第10帧发出了值
0,紧接着发出了完成通知(C) - 但你定义的预期流
cold('-c', {c: 0})只包含了发出值0的通知,没有完成通知
两者的行为不匹配,所以测试失败了。
为什么实际流会有完成通知?
当error=true时,do操作符在第一个值到来时抛出错误,catch操作符会捕获这个错误并返回Observable.of(0)。而RxJS里的Observable.of(0)的行为是:发出值0之后,立即发出完成信号,所以整个被测试的流会在发出0后直接结束。
解决方案
修改第二个测试用例的预期marble字符串,加上代表流完成的|符号:
it('should handle error', () => { const e1 = cold('-a-|', {a: 1}); const e2 = cold('--b-|', {b: 2}); // 这里加上|表示流完成 const expected = cold('-c|', {c: 0}); const obs: Observable<number> = service(e1, e2, true); expect(obs).toBeObservable(expected); });
修改后,预期流的行为就和实际流完全一致了:在第10帧发出值0,紧接着发出完成通知,测试就能通过。
补充说明
jasmine-marbles的marble语法里:
-代表时间帧- 字母(比如
c)代表发出的值 |代表流正常完成#代表流抛出错误
如果你的预期流没有写|,jasmine-marbles会认为这个流是永远不会结束的,但实际流因为of(0)的特性会立即结束,这就是测试失败的核心原因。
内容的提问来源于stack exchange,提问作者chihab

