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}}]
Based on the official docs for the
first
operator: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 hasnotification.kind
value ofC
which means that it is aC
omplete 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 emittrue
in thesource
observable. When you callmapFirstToStr(source)
, it gets throughfirst
operator and then throughswitchMap
. Arrow function inswitchMap
returns an observable with the value based on condition in ternary operator. Since the received value istrue
, ternary conditionval ? 'A' : 'B'
returns'A'
. Thus, the value inexpected
observable can not be'B'
(which is the value from the error logs), but'A'
.So, either you emit the first value
false
:Or you expect
'A'
: