如何使用RxJS Marbles测试发射分组事件的Observable?
测试带延迟的RxJS Observable:
of(1,2).concat(of(3,4).delay(20)) 首先先明确你提到的RxJS Marble同步分组的现有行为:
根据RxJS Marbles文档,同步分组的规则是:'(ab)-(cd)' 表示在第0帧发射a和b,随后在第50帧发射c和d。文档里特意说明:
虽然起初可能不太直观,但在所有值同步发射后,时间会推进与分组中ASCII字符数(包括括号)相等的帧数
现在回到你的核心问题:如何测试const observable$ = of(1, 2).concat(of(3, 4).delay(20));这个Observable?这里有几个实用的解决方法,覆盖Marble测试和传统异步测试两种场景:
方法1:使用精确时间标记的Marble测试
Marble测试默认每个-代表10帧,但如果你不想纠结同步分组的帧推进计算,可以直接用精确的时间标记(比如0ms、20ms)来定义预期序列,这样更直观也不容易出错。
关键是要把测试调度器传入delay操作符,这样Marble测试才能完全控制时间流,不会使用真实的延迟:
import { TestScheduler } from 'rxjs/testing'; import { of, concat } from 'rxjs'; import { delay } from 'rxjs/operators'; describe('Delayed Concat Observable Test', () => { let scheduler: TestScheduler; beforeEach(() => { scheduler = new TestScheduler((actual, expected) => { expect(actual).toEqual(expected); }); }); it('emits 1,2 immediately then 3,4 after 20ms', () => { scheduler.run(({ expectObservable }) => { const observable$ = of(1, 2).pipe( concat(of(3, 4).pipe(delay(20, scheduler))) // 传入测试调度器 ); // 用精确时间标记定义预期行为 expectObservable(observable$).toBe( '0ms (12), 20ms (34)', { 1: 1, 2: 2, 3: 3, 4: 4 } ); }); }); });
方法2:用cold Observable构建匹配序列
如果你更习惯Marble的符号语法,可以用cold来分别模拟两个子Observable的行为,然后组合起来验证:
scheduler.run(({ cold, expectObservable }) => { // 第一个Observable:同步发射1、2 const first$ = cold('(12)'); // 第二个Observable:延迟20帧后同步发射3、4(第三个参数指定时间偏移) const second$ = cold('(34)', {}, 20); const observable$ = first$.concat(second$); // 预期序列:先同步发射1、2,等待20帧后发射3、4 expectObservable(observable$).toBe('(12)------------------(34)', { 1: 1, 2: 2, 3: 3, 4: 4 }); });
这里的cold('(34)', {}, 20)表示这个Observable会在相对时间20帧的时候发射值,和第一个序列拼接后,正好匹配原Observable的行为。
方法3:传统异步测试(无需Marble)
如果Marble的时间规则让你觉得麻烦,也可以用Jasmine/Jest的异步测试来验证,虽然没法精确验证延迟时长,但能确保值的顺序和最终结果正确:
it('emits values in correct order', (done) => { const collectedValues: number[] = []; observable$.subscribe({ next: (val) => collectedValues.push(val), complete: () => { expect(collectedValues).toEqual([1, 2, 3, 4]); done(); }, error: done.fail }); });
重要注意事项
- 无论用哪种Marble测试方式,必须把测试调度器传入
delay操作符,否则delay会使用真实的浏览器/Node.js定时器,导致测试变慢甚至超时。 - 如果坚持用符号式Marble语法,要记得同步分组的帧推进规则:比如
(12)包含4个ASCII字符((、1、2、)),所以会自动推进4帧,计算中间的-数量时要把这个偏移考虑进去。
内容的提问来源于stack exchange,提问作者kazinov
相关产品推荐
相关产品推荐

