При тестировании JavaScript-программ часто возникает необходимость работать с системным временем. В реальных приложениях время используется для различных целей — от таймеров и анимаций до вычислений, зависящих от текущей даты. В таких случаях важно уметь “замораживать” или изменять время в тестах, чтобы обеспечить детерминированные и предсказуемые результаты.
Jest предоставляет возможность мокировать время, что позволяет точнее контролировать поведение тестируемых компонентов, зависящих от времени. Рассмотрим, как это реализуется в Jest.
Для работы с системным временем и таймерами Jest предоставляет несколько полезных функций:
setTimeout, setInterval и
другие.Обе функции полезны для того, чтобы манипулировать временем и точно контролировать, когда срабатывают события, зависящие от времени.
Одной из самых мощных функций для тестирования, связанного с
временем, является jest.setSystemTime(). С помощью этой
функции можно “заморозить” системное время на определённый момент и
использовать это время в тестах.
Пример:
jest.setSystemTime(new Date(2021, 0, 1)); // Устанавливаем дату 1 января 2021 года
test('проверка, что дата корректно установлена', () => {
const currentDate = new Date();
expect(currentDate.getFullYear()).toBe(2021);
expect(currentDate.getMonth()).toBe(0); // Январь (0)
});
Мокирование времени полезно, например, при тестировании функций, которые работают с датами и временем. Для того чтобы проверить их работу в различных временных контекстах, необходимо контролировать время на протяжении всех тестов.
Пример: тестирование функции, которая возвращает количество дней до ближайшей пятницы:
const getDaysUntilFriday = () => {
const today = new Date();
const daysUntilFriday = (5 - today.getDay() + 7) % 7;
return daysUntilFriday;
};
test('рассчитывает количество дней до пятницы', () => {
jest.setSystemTime(new Date(2021, 0, 1)); // Пятница, 1 января 2021 года
expect(getDaysUntilFriday()).toBe(0); // Пятница, следовательно, 0 дней
jest.setSystemTime(new Date(2021, 0, 4)); // Понедельник, 4 января 2021 года
expect(getDaysUntilFriday()).toBe(4); // Следующая пятница через 4 дня
});
Когда требуется тестировать функции, использующие асинхронные
таймеры, такие как setTimeout или setInterval,
можно воспользоваться функцией jest.useFakeTimers() для
симуляции работы таймеров.
Пример с использованием setTimeout:
jest.useFakeTimers();
test('проверка работы setTimeout', () => {
const mockCallback = jest.fn();
setTimeout(mockCallback, 1000);
// До того как таймер сработает, mockCallback не должен быть вызван
expect(mockCallback).not.toHaveBeenCalled();
// Прокачиваем таймеры до 1000 мс
jest.advanceTimersByTime(1000);
// Теперь mockCallback должен быть вызван
expect(mockCallback).toHaveBeenCalledTimes(1);
});
Когда тестируем асинхронные функции, которые зависят от времени
(например, с использованием setTimeout,
Promise, setInterval), важно правильно
использовать мокированные таймеры.
Пример:
jest.useFakeTimers();
const fetchData = (callback) => {
setTimeout(() => {
callback('data');
}, 2000);
};
test('проверка асинхронной функции с использованием setTimeout', () => {
const mockCallback = jest.fn();
fetchData(mockCallback);
// Проверяем, что коллбек ещё не был вызван
expect(mockCallback).not.toHaveBeenCalled();
// Прокачиваем таймеры
jest.advanceTimersByTime(2000);
// Теперь коллбек должен быть вызван
expect(mockCallback).toHaveBeenCalledWith('data');
});
После использования моков времени или таймеров важно не забывать об
их сбросе. Jest предоставляет функцию jest.clearAllMocks(),
чтобы очистить все моки, и jest.useRealTimers() для
возврата к реальным таймерам.
Пример:
afterEach(() => {
jest.clearAllMocks();
jest.useRealTimers();
});
Это необходимо для того, чтобы изменения, связанные с моком времени, не влияли на другие тесты, и каждый тест был изолирован.
mockdateДля ещё большего контроля над временем можно использовать сторонние
библиотеки, такие как mockdate. Эта библиотека позволяет
легко задавать конкретные даты и время.
Пример с использованием mockdate:
import MockDate from 'mockdate';
test('проверка работы с библиотекой mockdate', () => {
MockDate.set('2021-01-01'); // Устанавливаем фиксированную дату
const currentDate = new Date();
expect(currentDate.getFullYear()).toBe(2021);
expect(currentDate.getMonth()).toBe(0);
MockDate.reset(); // Сбрасываем изменения времени
});
jest.useFakeTimers() для
асинхронных функций, чтобы точно контролировать время
выполнения.Таким образом, мокирование времени в Jest — мощный инструмент для точного контроля работы с датами и таймерами, что необходимо для стабильных и предсказуемых тестов.