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
firstoperator:So, when your
obobservable 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.kindvalue ofCwhich means that it is aComplete 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
coldobservables you created, you emittruein thesourceobservable. When you callmapFirstToStr(source), it gets throughfirstoperator and then throughswitchMap. Arrow function inswitchMapreturns 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 inexpectedobservable 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':