angularjasminejasmine-marbles

jasmine-marbles next only emit first value


I've got some marbles like so:

import { cold, getTestScheduler } from 'jasmine-marbles'
const marbles$ = cold('--x--y|', {x: false, y: true})

When I call:

getTestScheduler().flush()

Both x and y are emitted. However, I want to do this:

it('my test', () => {
  // setup spies and other logic here
  const marbles$ = cold('--x--y|', {x: false, y: true})
  expect(foo).toBe(bar1)
  // EMIT x FROM marbles$ here
  expect(foo).toBe(bar2)
  // EMIT y FROM marbles$ here
  expect(foo).toBe(bar3)
})

Is this possible? If so how do I achieve this? Thx

What I am looking for is something like getTestScheduler().next() similar to how you would call next on an RxJs Subject - Maybe it would emit the next item in the marbles or it would emit nothing if the next item was '-' ... not exactly sure how it would work but hopefully you get the gist of what I am after.


Solution

  • Well, jasmine-marbles actually provides a very handy matcher for testing the output of a stream so you don't have to somehow manually trigger the scheduler: .toBeObservable. You use it by passing it another stream, the expected output.

    I'm going to alter your example slightly to show its use. Let's say we're testing a map from one stream to another in the real module, that takes a string and emits a boolean.

    // real-module.ts
    import { Observable, Subject } from 'rxjs';
    import { map } from 'rxjs/operators';
    
    export const input$: Subject<string> = new Subject ();
    export const output$: Observable<boolean> = input$.pipe (map (value => ({
        IWantTheTruth       : true,
        ICantHandleTheTruth : false
    }[value])));
    
    // real-module.spec.ts
    import { cold } from 'jasmine-marbles';
    import { input$, output$ } from './real-module';
    
    const schedule$ = cold ('--x--y|', { x : 'IWantTheTruth', y : 'ICantHandleTheTruth' });
    const expected$ = cold ('--x--y|', { x : true, y : false });
    
    schedule$.subscribe (input$);
    expect (output$).toBeObservable (expected$);
    

    The matcher runs the test scheduler for you, and compares the results of the actual and expected streams as if it's simply comparing two ordinary iterables. You can see this if you deliberately fail a test:

    expect (cold ('-x')).toBeObservable (cold ('x-'));
    

    The output error message from this failing test looks like this (I've added the newlines for clarity):

    Expected [
     Object({ frame: 10, notification: Notification({ kind: 'N', value: 'x', error: undefined, hasValue: true }) })
    ] to equal [
     Object({ frame: 0, notification: Notification({ kind: 'N', value: 'x', error: undefined, hasValue: true }) })
    ].
    

    You can see the value of frame is different, because of the different timings in the marbles. The Notification object shows the details of what was emitted. kind is one of 'N' for next, 'E' for error or 'C' for complete.