Timer Mocks
Las funciones de temporizador nativo (es decir, setTimeout
, setInterval
, clearTimeout
, clearInterval
) son ideales para un entorno de test puesto que dependen del transcurso del tiempo real. Jest puede cambiar temporizadores con funciones que permiten controlar el paso del tiempo. gran Scott!
// timerGame.js 'use strict'; function timerGame(callback) { console.log('Ready....go!'); setTimeout(() => { console.log('Times up -- stop!'); callback && callback(); }, 1000); } module.exports = timerGame;
// __tests__/timerGame-test.js 'use strict'; jest.useFakeTimers(); test('waits 1 second before ending the game', () => { const timerGame = require('../timerGame'); timerGame(); expect(setTimeout.mock.calls.length).toBe(1); expect(setTimeout.mock.calls[0][1]).toBe(1000); });
Aquí habilitamos las falsas alarmas mediante una llamada a jest.useFakeTimers();
. Esto mockea setTimeout y otras funciones de temporizador con funciones simuladas.
Ejecutar todos los temporizadores #
Otra prueba que podríamos querer escribir para este módulo es la que afirma que la devolución de la llamada se realiza después de 1 segundo. Para ello, vamos a utilizar control de temporizador de Jest APIs para adelantar la hora correcta en el medio de la prueba:
test('calls the callback after 1 second', () => { const timerGame = require('../timerGame'); const callback = jest.fn(); timerGame(callback); // At this point in time, the callback should not have been called yet expect(callback).not.toBeCalled(); // Fast-forward until all timers have been executed jest.runAllTimers(); // Now our callback should have been called! expect(callback).toBeCalled(); expect(callback.mock.calls.length).toBe(1); });
Ejecutar temporizadores pendientes #
También hay escenarios donde se podría tener un temporizador recursivo, que es un contador de tiempo que establece un nuevo temporizador en su propio callback. Para éstos, corriendo todos los contadores de tiempo sería un bucle sin fin... por lo que algo como jest.runAllTimers()
no es deseable. Para estos casos se podría utilizar jest.runOnlyPendingTimers()
:
// infiniteTimerGame.js 'use strict'; function infiniteTimerGame(callback) { console.log('Ready....go!'); setTimeout(() => { console.log('Times up! 10 seconds before the next game starts...'); callback && callback(); // Schedule the next game in 10 seconds setTimeout(() => { infiniteTimerGame(callback); }, 10000); }, 1000); } module.exports = infiniteTimerGame;
// __tests__/infiniteTimerGame-test.js 'use strict'; jest.useFakeTimers(); describe('infiniteTimerGame', () => { test('schedules a 10-second timer after 1 second', () => { const infiniteTimerGame = require('../infiniteTimerGame'); const callback = jest.fn(); infiniteTimerGame(callback); // At this point in time, there should have been a single call to // setTimeout to schedule the end of the game in 1 second. expect(setTimeout.mock.calls.length).toBe(1); expect(setTimeout.mock.calls[0][1]).toBe(1000); // Fast forward and exhaust only currently pending timers // (but not any new timers that get created during that process) jest.runOnlyPendingTimers(); // At this point, our 1-second timer should have fired it's callback expect(callback).toBeCalled(); // And it should have created a new timer to start the game over in // 10 seconds expect(setTimeout.mock.calls.length).toBe(2); expect(setTimeout.mock.calls[1][1]).toBe(10000); }); });
Temporizadores de tiempo de funcionamiento #
Otra posibilidad es usar jest.runTimersToTime(msToRun)
. Cuando se llama a la API, todas las "macro-tareas pendientes" que han sido cola mediante setTimeout() o setInterval() y se ejecutaría dentro de milisegundos de la msToRun, serán ejecutadas. Además si esas macro tareas programan nuevas macro tareas que se ejecutarían dentro del mismo plazo, aquellas se ejecutará hasta que no haya más macro-tareas restantes en la cola que debe realizarse dentro de los msToRun milisegundos.
// timerGame.js 'use strict'; function timerGame(callback) { console.log('Ready....go!'); setTimeout(() => { console.log('Times up -- stop!'); callback && callback(); }, 1000); } module.exports = timerGame;
it('calls the callback after 1 second via runTimersToTime', () => { const timerGame = require('../timerGame'); const callback = jest.fn(); timerGame(callback); // At this point in time, the callback should not have been called yet expect(callback).not.toBeCalled(); // Fast-forward until all timers have been executed jest.runTimersToTime(1000); // Now our callback should have been called! expect(callback).toBeCalled(); expect(callback.mock.calls.length).toBe(1); });
Por último, en ocasiones puede ser útil en algunas pruebas para poder borrar todos los contadores de tiempo pendientes. Para ello, contamos con jest.clearAllTimers()
.
El código de este ejemplo está disponible en: examples/timer.