Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test that an Observable does not emit in Angular Component which contains asynchronous logic

Let's say I have an Angular Component which defines an Observable myObs$ as one of its properties.

In one test, given certain conditions, I want to test that myObs$ does not notify. In the logic there are some delays involved, so the test must be asynchronous.

I am using Jasmine.

So far I have been able to craft this solution:

it('async test', done => {
    let dataEmitted;
    myObs$
    .pipe(
      tap(data => dataEmitted = data),
    )
    .subscribe();
    setTimeout(() => {
      if (dataEmitted) {
        done.fail('should not emit');
      } else {
        done();
      }
    }, 1000);
  });

But I am far from happy with it. I have to rely on setTimeout to perform the checks and call the done function.

Any suggestions on how to perform such tests properly? Synchronous solutions do not work since there is intrinsic asynchronicity in the logic.

like image 932
Picci Avatar asked Jan 30 '19 18:01

Picci


People also ask

Are observables asynchronous?

An observable produces values over time. An array is created as a static set of values. In a sense, observables are asynchronous where arrays are synchronous.

What does fixture detectChanges () do?

detectChanges(). Delayed change detection is intentional and useful. It gives the tester an opportunity to inspect and change the state of the component before Angular initiates data binding and calls lifecycle hooks.

How do you write a test case for observable in jest?

Testing a Single Emitted Value You can then write your expectations inside of the the subscribe callback, then call done() when you're ready for the test to finish. import { of } from 'rxjs'; test('the observable emits hello', done => { of('hello'). subscribe( data => { expect(data). toBe('hola'); done(); }); });


2 Answers

If it's asynchronous logic that based on setTimeout/debounceTime and so forth, you can use fakeAsync() function to test it, this function will substitute all these asynchronous operations with synchronous ones, so it will be possible to test your logic since it's synchronous. Also you can use tick() to skip VM turns (and it happens also synchronously!). Using this solution you will have nice and clean, fast and reliable unit tests.

Code example

it('asynch test', fakeAsync(() => {
    let dataEmitted;
    myObs$.subscribe(data => dataEmitted = data);

    tick(1000);
    
    expect(dataEmitted).toBeUndefined();
}));

I suggest you also to check negative scenario, for example tick(2000) and check if it emits value. Hope that helps.

like image 92
Amir Arbabian Avatar answered Oct 25 '22 18:10

Amir Arbabian


Assuming that the Observable eventually completes, this can also be done with the conventional it function by using done():

it(
  'async test',
  (done: DoneFn) => {
    let emitted = false;
    myObs$
    .subscribe({
      next: () => emitted = true;
      complete: () => {
        expect(emitted).toBeFalse();
        done();
      }
    });
  }
);
like image 32
Joseph Zabinski Avatar answered Oct 25 '22 18:10

Joseph Zabinski