0

I don't understand why .getValue() returns the default value of the Observable and not the last value emitted. When testing the Observable, it correctly returns the emitted value.

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);
  });
});

To clarify, the getValue() method works fine outsude tests, it only fails while testing with Jest.

Thanks!

skyboyer
  • 22,209
  • 7
  • 57
  • 64
Marçal Juan
  • 1,302
  • 14
  • 20

3 Answers3

0

The reason is asynchronous nature of callback function of catch. So I think if you wrap your expect statement in setTimeout , and run test as async it became green.

0

The first assertion is asynchronous. Internally, it'll resolve the Observable, so you'll indeed get 2.

However, while this is pending, the other two assertions are triggered. And they are synchronous. Nothing guarantee you that, at that time, the .next call has been done. So you still get the initial value.

That's why I would recommend to not use the .getValue method of BehaviorSubject and rather subscribe to it properly. That way, you'll avoid such confusion by always doing asynchronous operations.

frankie567
  • 1,703
  • 12
  • 20
  • I would rather aside of subscribing to the Observable do more tests that contains the wrapper function, so since `Promise.reject()` is not sync I need to flush microtasks and then it passes all tests. – Marçal Juan Sep 24 '19 at 12:19
0

Even if I do Promise.reject() the code doesn't turnout to be synchronous, so in this case you need to flush execution queue in order to test that code.

A solution using Angular helper functions would be:

  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);
  }));
Marçal Juan
  • 1,302
  • 14
  • 20