You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:27:40