Timer-mokker
This page was AI-translated by PageTurner (beta). Not officially endorsed by the project. Found an error? Report issue →
De native timer-funksjonene (f.eks. setTimeout(), setInterval(), clearTimeout(), clearInterval()) er ikke ideelle i testmiljøer siden de avhenger av faktisk tidspassering. Jest kan erstatte timere med funksjoner lar deg kontrollere tidens gang. Great Scott!
Se også dokumentasjonen for Falske Timere API.
Aktiver falske timere
I følgende eksempel aktiverer vi falske timere ved å kalle jest.useFakeTimers(). Dette erstatter den opprinnelige implementasjonen av setTimeout() og andre timerfunksjoner. Timere kan gjenopprettes til normal oppførsel med jest.useRealTimers().
function timerGame(callback) {
console.log('Ready....go!');
setTimeout(() => {
console.log("Time's up -- stop!");
callback && callback();
}, 1000);
}
module.exports = timerGame;
jest.useFakeTimers();
jest.spyOn(global, 'setTimeout');
test('waits 1 second before ending the game', () => {
const timerGame = require('../timerGame');
timerGame();
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
});
Kjør alle timere
En annen test vi kanskje vil skrive for denne modulen er en som bekrefter at tilbakekallingen skjer etter 1 sekund. For å gjøre dette bruker vi Jests API for tidskontroll for å spole tiden fremover midt i testen:
jest.useFakeTimers();
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.toHaveBeenCalled();
// Fast-forward until all timers have been executed
jest.runAllTimers();
// Now our callback should have been called!
expect(callback).toHaveBeenCalled();
expect(callback).toHaveBeenCalledTimes(1);
});
Kjør ventende timere
Det finnes også scenarier hvor du kan ha en rekursiv timer – det vil si en timer som setter en ny timer i sin egen tilbakekalling. For disse vil kjøring av alle timere skape en endeløs løkke, som gir feilen: "Avbryter etter å ha kjørt 100000 timere, antar uendelig løkke!"
Hvis dette gjelder deg, vil bruk av jest.runOnlyPendingTimers() løse problemet:
function infiniteTimerGame(callback) {
console.log('Ready....go!');
setTimeout(() => {
console.log("Time's 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;
jest.useFakeTimers();
jest.spyOn(global, 'setTimeout');
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).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 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 its callback
expect(callback).toHaveBeenCalled();
// And it should have created a new timer to start the game over in
// 10 seconds
expect(setTimeout).toHaveBeenCalledTimes(2);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000);
});
});
For feilsøking eller andre grunner kan du endre grensen for antall timere som kjøres før feil kastes:
jest.useFakeTimers({timerLimit: 100});
Forfrem timere med tid
Et annet alternativ er å bruke jest.advanceTimersByTime(msToRun). Når dette API-et kalles, forfremmes alle timere med msToRun millisekunder. Alle ventende "makro-oppgaver" i køen fra setTimeout() eller setInterval() som skulle utføres i dette tidsvinduet, vil kjøres. Videre, hvis disse makro-oppgavene planlegger nye makro-oppgaver som skal utføres i samme tidsvindu, vil disse også kjøres til ingen flere makro-oppgaver gjenstår i køen for angitt tidsvindu.
function timerGame(callback) {
console.log('Ready....go!');
setTimeout(() => {
console.log("Time's up -- stop!");
callback && callback();
}, 1000);
}
module.exports = timerGame;
jest.useFakeTimers();
it('calls the callback after 1 second via advanceTimersByTime', () => {
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.toHaveBeenCalled();
// Fast-forward until all timers have been executed
jest.advanceTimersByTime(1000);
// Now our callback should have been called!
expect(callback).toHaveBeenCalled();
expect(callback).toHaveBeenCalledTimes(1);
});
Til slutt kan det av og til være nyttig i tester å kunne tømme alle ventende timere. Til dette har vi jest.clearAllTimers().
Selektiv forfalskning
Noen ganger kan koden din kreve å unngå å overskrive den originale implementasjonen av et bestemt API. I så fall kan du bruke doNotFake-alternativet. For eksempel, slik kan du gi en tilpasset mock-funksjon for performance.mark() i jsdom-miljø:
/**
* @jest-environment jsdom
*/
const mockPerformanceMark = jest.fn();
window.performance.mark = mockPerformanceMark;
test('allows mocking `performance.mark()`', () => {
jest.useFakeTimers({doNotFake: ['performance']});
expect(window.performance.mark).toBe(mockPerformanceMark);
});