angularrxjsjasmine-marblesrxjs-marbles

Test observable 'next' callback in RxJs and Angular


I'm trying to do a pretty simple test in Angular with RxJs Observables, but I'm coming up short. This is what I'm basically trying to test:

// We're inside some Angular component here...
let testMe = 0;

function somethingOrOther(): void {

    this.someService.methodReturningObservable()
      .subscribe(
          (nextValue: number): void => {
              testMe += nextValue;
          }
      ) 
}

How do I test that testMe gets updated properly when the observable behind methodReturningObservable emits a value?

I attempted it with this:

it(`works 'cuz I want it to`, fakeAsync(() => {
    spyOn(this.someService, 'methodReturningObservable').and.returnValue(cold('a', {a: 10}));

    tick();

    expect(component.testMe).toBe(10);
}));

So, tick() doesn't seem to do anything here. Nothing gets my cold value to ever emit on my spy.

I tried getTestScheduler.flush() instead as shown at https://netbasal.com/testing-observables-in-angular-a2dbbfaf5329 under the marbles section.

Can I use marbles like this to emit a value on an observable? This was very easy in AngularJS by just triggering a digest, but I can't seem to get Anguar to let me in the next callback for an observable.


Solution

  • I put together a simple Stackblitz to show you how I would approach testing that simple component method you describe.

    Here is the spec from that Stackblitz:

    it(`works with "of" 'cuz I want it to`, () => {
        spyOn(someService, 'methodReturningObservable').and.returnValue(of(10));
        component.somethingOrOther();
        expect(component.testMe).toBe(10);
    });
    

    Some of the changes I made to your code:

    As you can see in the Stackblitz, this test passes just fine.

    Now, if you want to use marble testing even for this simple case, I set up another spec in the Stackblitz for that. It is as follows:

    it(`works with "cold" 'cuz I want it to`, () => {
        spyOn(someService, 'methodReturningObservable').and.returnValue(cold('a', { a: 10 }));
        component.somethingOrOther();
        getTestScheduler().flush(); // flush the observables
        expect(component.testMe).toBe(10);
    });
    

    Both tests are now passing in the Stackblitz.

    Official docs for marble testing in Angular are here.

    I hope this helps.