Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing Observables with jest

How can I test Observables with Jest?

I have an Observable that fires ~every second, and I want to test that the 1st event is correctly fired, before jest times out.

const myObservable = timer(0, 1000); // Example here

it('should fire', () => {
  const event = myObservable.subscribe(data => {
    expect(data).toBe(0);
  });
});

This test passes, but it also passes if I replace with toBe('anything'), so I guess I am doing something wrong.

I tried using expect.assertions(1), but it seems to be only working with Promises.

like image 815
jeanpaul62 Avatar asked Jun 05 '18 10:06

jeanpaul62


People also ask

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(); }); });

Do jest tests run asynchronously?

It's common in JavaScript for code to run asynchronously. When you have code that runs asynchronously, Jest needs to know when the code it is testing has completed, before it can move on to another test.

How do observables work?

The Observable is initialized with a function. This function is the observer . The observer gets called with an object, the subscriber . Once the observer function gets called with the subscriber object, it can call three methods in this subscriber — the next , error , and complete methods.


4 Answers

There are some good examples in the Jest documentation about passing in an argument for the test. This argument can be called to signal a passing test or you can call fail on it to fail the test, or it can timeout and fail.

https://jestjs.io/docs/en/asynchronous.html

https://alligator.io/testing/asynchronous-testing-jest/

Examples

Notice I set the timeout to 1500ms

const myObservable = timer(0, 1000); // Example here

it('should fire', done => {
  myObservable.subscribe(data => {
    done();
  });
}, 1500); // Give 1500ms until it fails

Another way to see if it fails using setTimeout

const myObservable = timer(0, 1000); // Example here

it('should fire', done => {
  myObservable.subscribe(data => {
    done();
  });

  // Fail after 1500ms
  setTimeout(() => { done.fail(); }, 1500);
}, timeToFail);
like image 88
CTS_AE Avatar answered Oct 04 '22 11:10

CTS_AE


My preferred way to test observables, without fake timers and timeouts, is to async, await and use resolves or rejects on an expected converted promise.

it('should do the job', async () => {
    await expect(myObservable
      .pipe(first())
      .toPromise())
      .resolves.toEqual(yourExpectation);
});

Update:

In Rxjs 7 and onwards, you can use lastValueFrom or firstValueFrom for the promise convertion

it('should do the job', async () => {
    await expect(lastValueFrom(myObservable))
      .resolves.toEqual(yourExpectation);
});

like image 37
ginalx Avatar answered Oct 04 '22 10:10

ginalx


test('Test name', (done) => {
  service.getAsyncData().subscribe((asyncData)=>{
    expect(asyncData).toBeDefined();
       done();
    })
  });
})
like image 30
Yoav Schniederman Avatar answered Oct 04 '22 09:10

Yoav Schniederman


Since version 6.2.1, RxJS supports Jest's fake time, so one way you could write this test is:

const myObservable = timer(0, 1000);

jest.useFakeTimers('modern');

it('should fire', () => {
  myObservable.subscribe(data => {
    expect(data).toBe(0);
  });
  jest.runAllTimers();
});

I've published a library that helps with this type of tests (in the below example, log adds logging to the observable, and getMessages retrieves messages that have been logged):

import { getMessages, log } from '1log';
import { timer } from 'rxjs';

const myObservable = timer(0, 1000);

test('timer', () => {
  myObservable.pipe(log).subscribe();
  jest.runAllTimers();
  expect(getMessages()).toMatchInlineSnapshot(`
    [create 1] +0ms [Observable]
    [create 1] [subscribe 1] +0ms [Subscriber]
    [create 1] [subscribe 1] [next] +1.000s 0
    [create 1] [subscribe 1] [complete] +0ms
    · [create 1] [subscribe 1] [unsubscribe] +0ms
  `);
});
like image 5
Ivan Avatar answered Oct 04 '22 11:10

Ivan