J'essaie d'écrire un test unitaire pour la fonction anti-rebond. J'ai du mal à y penser.
Voici le 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);
};
}
Comment dois-je commencer?
Vous voudrez probablement vérifier la logique de votre fonction anti-rebond:
timeout
sera toujours défini par la dernière instruction if()
this
sera toujours undefined
puisque les fonctions flèches utilisent "la valeur this
du contexte lexical englobant" et debouncer()
est conçu pour être utilisé comme une fonction autonome.Cela dit, il semble que votre vraie question concerne le test des fonctions anti-rebond.
Vous pouvez tester qu'une fonction est anti-rebond en utilisant une maquette pour suivre les appels de fonction et de faux minuteries pour simuler le passage du temps.
Voici un exemple simple utilisant une fonction Jest
Mock Function et Sinon
fake timers d'une fonction rebondie en utilisant debounce()
de 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
});
En fait, vous n'avez pas besoin d'utiliser Sinon pour tester les rebonds. Jest peut se moquer de tous les temporisateurs en code JS.
Consultez le code suivant (c'est TypeScript, mais vous pouvez facilement le traduire en 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);
});
});
Plus d'informations: https://jestjs.io/docs/en/timer-mocks.html
Si dans votre code vous le faites:
import debounce from 'lodash/debounce';
myFunc = debounce(myFunc, 300);
et vous voulez tester la fonction myFunc
ou une fonction qui l'appelle, alors dans votre test vous pouvez vous moquer de l'implémentation de debounce
en utilisant jest
pour qu'elle revienne simplement à votre fonction:
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
Je ne sais pas comment cela fonctionnerait avec Jest puisque j'utilise du moka, mais pour tous ceux qui recherchent une solution simple:
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);
});