Test observable mapping using marbles

417 Views Asked by At

I have a method that takes an observable as an input and switches to new observable. Alternatively, I could use map instead of switchMap. How can this be tested using marbles?

mapFirstToStr(ob: Observable<boolean>): Observable<string> {
   return ob.pipe(
       first(),
       switchMap(val => of(val ? 'A' : 'B'))
   );
}

This doesn't work:

const source = cold('a', { a: true });
const expected = cold('b', { b: 'B' });
expect(mapFirstToStr(source)).toBeObservable(expected);

I get the following error:

Expected value to equal: [{"frame": 0, "notification": {"error": undefined, "hasValue": true, > "kind": "N", "value": "B"}}]

Received: [{"frame": 0, "notification": {"error": undefined, "hasValue": true, > "kind": "N", "value": "B"}}, {"frame": 0, "notification": {"error": > undefined, "hasValue": false, "kind": "C", "value": undefined}}]

1

There are 1 best solutions below

0
On

Based on the official docs for the first operator:

If called with no arguments, first emits the first value of the source Observable, then completes.

So, when your ob observable emits the first value, the stream immediately completes. From the error log you printed here, you can see that array that was received contains two objects. They are both emitted on the same frame (0), and the second one has notification.kind value of C which means that it is a Complete event.

So, you have to have expected marbles like this: '(b|)'.

The one thing that bothers me is that the output values do not match. Based on the two cold observables you created, you emit true in the source observable. When you call mapFirstToStr(source), it gets through first operator and then through switchMap. Arrow function in switchMap returns an observable with the value based on condition in ternary operator. Since the received value is true, ternary condition val ? 'A' : 'B' returns 'A'. Thus, the value in expected observable can not be 'B' (which is the value from the error logs), but 'A'.

So, either you emit the first value false:

const source = cold('a', { a: false });
const expected = cold('(b|)', { b: 'B' });
expect(mapFirstToStr(source)).toBeObservable(expected);

Or you expect 'A':

const source = cold('a', { a: true });
const expected = cold('(b|)', { b: 'A' });
expect(mapFirstToStr(source)).toBeObservable(expected);