发出值后 RxJS BehaviorSubject getValue 不一致(在 Jest 中测试时)

RxJS BehaviorSubject getValue inconsistency after value emitted (while testing in Jest)

我不明白为什么 .getValue() returns Observable 的默认值而不是最后发出的值。在测试 Observable 时,它​​正确 returns 发出的值。

class TestA {
  readonly aSource: BehaviorSubject<number> = new BehaviorSubject(null);

  getA(): number {
    return this.aSource.getValue();
  }

  promise(): void {
    Promise.reject()
      .catch(() => {
        this.aSource.next(2);

        console.log(this.getA()); // Outputs: 2
      });
  }
}

describe('TestA', () => {
  it('promise', () => {
    const a = new TestA();
    a.promise();

    // Test 1 OK
    expect(a.aSource.asObservable()).toBeObservable(hot('a', {a: 2}));

    // Test 2 FAIL (returns null)
    expect(a.aSource.getValue()).toEqual(2);

    // Test 3 FAIL (returns null)
    expect(a.getA()).toEqual(2);
  });
});

澄清一下,getValue() 方法在测试之外运行良好,它仅在使用 Jest 测试时失败。

谢谢!

原因是 catch 的回调函数的异步性质。所以我认为如果你将你的期望语句包装在 setTimeout 中,并且 运行 测试为异步它变成绿色。

第一个断言是异步的。在内部,它会解析 Observable,所以你确实会得到 2.

然而,当这待定时,其他两个断言被触发。而且它们是同步的。没有任何东西可以保证 .next 调用已经完成。所以你得到的还是初始值。

这就是为什么我建议不要使用 BehaviorSubject.getValue 方法,而是正确地订阅它。这样,您就可以通过始终执行异步操作来避免此类混淆。

即使我这样做 Promise.reject() 代码也不是同步的,所以在这种情况下你需要刷新执行队列以测试该代码。

使用 Angular 辅助函数的解决方案是:

  it('promise', fakeAsync(() => {
    const a = new TestA();
    a.promise();

    flush();

    expect(a.aSource.asObservable()).toBeObservable(hot('a', {a: 2}));
    expect(a.aSource.getValue()).toEqual(2);
    expect(a.getA()).toEqual(2);
  }));