Jest unit test for a debounce function
Actually, you don't need to use Sinon to test debounces. Jest can mock all timers in JavaScript code.
Check out following code (it's TypeScript, but you can easily translate it to JavaScript):
import * as _ from 'lodash';
// Tell Jest to mock all timeout functions
jest.useFakeTimers();
describe('debounce', () => {
let func: jest.Mock;
let debouncedFunc: Function;
beforeEach(() => {
func = jest.fn();
debouncedFunc = _.debounce(func, 1000);
});
test('execute just once', () => {
for (let i = 0; i < 100; i++) {
debouncedFunc();
}
// Fast-forward time
jest.runAllTimers();
expect(func).toBeCalledTimes(1);
});
});
More information: Timer Mocks
If in your code you are doing so:
import debounce from 'lodash/debounce';
myFunc = debounce(myFunc, 300);
and you want to test the function myFunc
or a function calling it, then in your test you can mock the implementation of debounce
using jest
to make it just return your function:
import debounce from 'lodash/debounce';
// Tell Jest to mock this import
jest.mock('lodash/debounce');
it('my test', () => {
// ...
debounce.mockImplementation(fn => fn); // Assign the import a new implementation. In this case it's to execute the function given to you
// ...
});
Source: https://gist.github.com/apieceofbart/d28690d52c46848c39d904ce8968bb27
You will probably want to check the logic in your debouncer function:
timeout
will always be set by that lastif()
statementthis
will always beundefined
since arrow functions use "thethis
value of the enclosing lexical context" anddebouncer()
is designed to be used as a stand-alone function.
Having said that, it sounds like your real question is about testing debounced functions.
Testing debounced functions
You can test that a function is debounced by using a mock to track function calls and fake timers to simulate the passage of time.
Here is a simple example using a Jest
Mock Function and Sinon
fake timers of a function debounced using debounce()
from Lodash
:
const _ = require('lodash');
import * as sinon from 'sinon';
let clock;
beforeEach(() => {
clock = sinon.useFakeTimers();
});
afterEach(() => {
clock.restore();
});
test('debounce', () => {
const func = jest.fn();
const debouncedFunc = _.debounce(func, 1000);
// Call it immediately
debouncedFunc();
expect(func).toHaveBeenCalledTimes(0); // func not called
// Call it several times with 500ms between each call
for(let i = 0; i < 10; i++) {
clock.tick(500);
debouncedFunc();
}
expect(func).toHaveBeenCalledTimes(0); // func not called
// wait 1000ms
clock.tick(1000);
expect(func).toHaveBeenCalledTimes(1); // func called
});