Jest unit test for a debounce function

你说的曾经没有我的故事 提交于 2019-12-06 01:39:18

问题


I am trying to write a unit test for debounce function. I'm having a hard time thinking about it.

This is the code:

function debouncer(func, wait, immediate) {
  let timeout;

  return (...args) => {
    clearTimeout(timeout);

    timeout = setTimeout(() => {
      timeout = null;
      if (!immediate) func.apply(this, args);
    }, wait);

    if (immediate && !timeout) func.apply(this, args);
  };
}

How should I start?


回答1:


You will probably want to check the logic in your debouncer function:

  • timeout will always be set by that last if() statement
  • this will always be undefined since arrow functions use "the this value of the enclosing lexical context" and debouncer() 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
});



回答2:


Actually, you don't need to use Sinon to test debounces. Jest can mock all timers in JS code.

Check out following code (it's TypeScript, but you can easily translate it to JS):

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: https://jestjs.io/docs/en/timer-mocks.html




回答3:


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 execute the function given to you
    // ...
});

Source: https://gist.github.com/apieceofbart/d28690d52c46848c39d904ce8968bb27




回答4:


Not sure how this would work with jest since i'm using mocha but for anyone out there looking a simple solution:

it('debounce.spec', done => {
    // Arrange
    const log = sinon.spy();
    const search = debounce(log, 100);

    // Act
    search();

    // Assert
    setTimeout(() => {
      expect(log.called).to.be.true;
      done();
    }, 100);
  });


来源:https://stackoverflow.com/questions/52224447/jest-unit-test-for-a-debounce-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!