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.
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(); }); });
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.
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.
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/
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);
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);
});
test('Test name', (done) => {
service.getAsyncData().subscribe((asyncData)=>{
expect(asyncData).toBeDefined();
done();
})
});
})
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
`);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With