RxJs Marble 使用 withLatestFrom 测试 concatMap

RxJs Marble testing concatMap with withLatestFrom

如何对这个 Observable 进行单元测试?

e1.pipe(
    concatMap(x => of(x).pipe(withLatestFrom(e2)))
);

以下单元测试失败:

        it('test', () => {
            const e1 = hot(       '--1^---2----3-|');
            const e2 = hot(       '-a-^-b-----c--|');
            const expected = cold(   '----x----y-|', {
                x: ['2', 'b'],
                y: ['3', 'c']
            });

            const result = e1.pipe(
                concatMap(x => of(x).pipe(
                    withLatestFrom(e2))
                )
            );

            // but this works:
            // const result = e1.pipe(withLatestFrom(e2));

            expect(result).toBeObservable(expected);
        });

弹珠应该怎么写才能通过这个单元测试?我做错了什么? 我希望通过在链中插入 concatMap 运算符(在 withLatestFrom 之前),我还必须以某种方式在弹珠中“标记”它。

在你的真实示例中

e1.pipe(
  concatMap(x => of(x).pipe(withLatestFrom(e2)))
);

一切正常,可能是因为是 BehaviorSubjectReplaySubject,但在您的测试中并非如此。

虽然您使用的是 hot( '-a-^-b-----c--|');,但这并不意味着您使用的是 BehaviorSubject。如果我们查看 implementation,我们会看到 HotObservable 扩展了 Subject class:

export class HotObservable<T> extends Subject<T> implements SubscriptionLoggable { /* ... */ }

这应该有助于理解为什么会这样:

const result = e1.pipe(withLatestFrom(e2));

而这不是:

const result = e1.pipe(
    concatMap(x => of(x).pipe(
        withLatestFrom(e2))
    )
);

在第一个片段中,e2e1 被订阅时被订阅。在第二个中,因为你正在使用 concatMap,每次 e1 发出时,withLatestFrom(e2)) 将被订阅然后取消订阅,因为 complete 通知来自 of(x).

考虑到这一点,我的方法是:

注意:我正在使用 rxjs/testing

提供的 built-in 函数
it('test', () => {

  // might want to add this in a `beforeEach` function
  let testScheduler = new TestScheduler(
    (actual, expected) => (console.log({actual, expected}),expect(actual).toEqual(expected))
  );

  testScheduler.run(({ hot, expectObservable }) => {
    const e1 = hot(       '--1^---2----3-|');
    const e2src = hot(       '-a-^-b-----c--|');
    const e2 = new BehaviorSubject(undefined);

    const result = e1.pipe(
        concatMap(x => of(x).pipe(
            withLatestFrom(e2))
        )
    );

    const source = merge(
      result,

      e2src.pipe(
        tap(value => e2.next(value)),
        
        // this is important as we're not interesting in `e2src`'s values
        // it's just a way to `feed` the `e2` BehaviorSubject
        ignoreElements()
      )
    );
    
    expectObservable(source).toBe('----x----y-|', {
      x: ['2', 'b'],
      y: ['3', 'c']
    });
  });
})