Is there easy way to mock delay()
method of RxJS in an observable with a fake time for example ?
I have this method :
register(user) {
return this._checkLog(user).delay(500).flatMap( ... )
}
when i remove delay()
method, my tests from _register() all success.
Since version 6.2.1, RxJS supports Jest's fake time, so you can simply write
jest.useFakeTimers('modern');
test('...', () => {
// ...code that subscribes to your observable.
jest.runAllTimers();
// ...code that makes assertions.
});
Also, I've published a library that makes writing tests like these easier, here's an example (log
adds logging to an observable, getMessages
retrieves logged messages):
import { getMessages, log } from '1log';
import { of } from 'rxjs';
import { delay } from 'rxjs/operators';
test('delay', () => {
of(42).pipe(delay(500), log).subscribe();
jest.runAllTimers();
expect(getMessages()).toMatchInlineSnapshot(`
[create 1] +0ms [Observable]
[create 1] [subscribe 1] +0ms [Subscriber]
[create 1] [subscribe 1] [next] +500ms 42
[create 1] [subscribe 1] [complete] +0ms
· [create 1] [subscribe 1] [unsubscribe] +0ms
`);
});
to complete brian-live-outdoor solution for RxJS 6, you can also mock the real behavior of delay() using delayWhen and timer which work with jest :
jest.mock("rxjs/operators", () => {
const operators = jest.requireActual("rxjs/operators");
const observables = jest.requireActual("rxjs");
operators.delay = jest.fn(delay => s =>
s.pipe(operators.delayWhen(() => observables.timer(delay)))
);
return operators;
});
and you can put this mock next to the node_modules folder :
.
├── __mocks__
│ └── rxjs
│ └── operators.js
└── node_modules
// operators.js
const operators = require("rxjs/operators");
const observables = require("rxjs");
operators.delay = jest.fn(delay => s =>
s.pipe(operators.delayWhen(() => observables.timer(delay)))
);
module.exports = operators;
An example of test which didn't worked before and work with the mock:
it("some test with delay()", (done: DoneFn) => {
let check = false;
jest.useFakeTimers();
of(true)
.pipe(delay(1000))
.subscribe(() => (check = true));
setTimeout(() => {
expect(check).toBe(true);
done();
}, 2000);
jest.runTimersToTime(999);
expect(check).toBe(false);
jest.runAllTimers();
});
For RxJS v6 code like this:
code.js
import { of } from 'rxjs';
import { delay } from 'rxjs/operators';
export const example = of('hello').pipe(
delay(1000)
);
...you can use sinon
fake timers like this:
import * as sinon from 'sinon';
import { example } from './code';
describe('delay', () => {
let clock;
beforeEach(() => { clock = sinon.useFakeTimers(); });
afterEach(() => { clock.restore(); });
it('should delay one second', () => {
const spy = jest.fn();
example.subscribe(spy);
expect(spy).not.toHaveBeenCalled(); // Success!
clock.tick(1000);
expect(spy).toHaveBeenCalledWith('hello'); // Success!
});
});
(Note that at time of writing Jest
timer mocks don't work, not sure why)
...or you can mock delay
to do nothing like this:
import { delay } from 'rxjs/operators';
import { example } from './code';
jest.mock('rxjs/operators', () => {
const operators = jest.requireActual('rxjs/operators');
operators.delay = jest.fn(() => (s) => s); // <= mock delay
return operators;
});
describe('delay', () => {
it('should delay one second', () => {
const spy = jest.fn();
example.subscribe(spy);
expect(delay).toHaveBeenCalledWith(1000); // Success!
expect(spy).toHaveBeenCalledWith('hello'); // Success!
});
});
For RxJS v5 code like this:
code.js
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/delay';
export const example = Observable.of('hello').delay(1000);
...you can mock delay
to do nothing like this:
import { Observable } from 'rxjs/Observable';
import { example } from './code';
jest.mock('rxjs/add/operator/delay', () => {
const Observable = require('rxjs/Observable').Observable;
Observable.prototype.delay = jest.fn(function () { return this; }); // <= mock delay
});
describe('delay', () => {
it('should delay one second', () => {
const spy = jest.fn();
example.subscribe(spy);
expect(Observable.prototype.delay).toHaveBeenCalledWith(1000); // Success!
expect(spy).toHaveBeenCalledWith('hello'); // Success!
});
});
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