Компоненты календаря относятся к числу наиболее чувствительных к текущей дате и времени. Любая зависимость от системного времени приводит к нестабильности тестов: поведение изменяется от дня к дню, от часового пояса к часовому поясу, а также при запуске в CI-средах.
В календарных UI-библиотеках, таких как Pikaday, текущее время используется для вычисления:
Отсутствие контроля над источником времени делает невозможным воспроизводимое тестирование, поэтому мокирование дат становится обязательной частью стратегии проверки календарной логики.
В JavaScript существует несколько способов получения текущего времени:
Date.now()
new Date()
new Date().getTime()
Все эти способы в конечном итоге зависят от системных часов среды выполнения. Это создаёт ряд проблем:
Pikaday использует Date для вычисления текущего месяца и
выделения актуального дня, поэтому любые изменения системного времени
напрямую влияют на рендер.
Внутри календарного компонента текущая дата влияет на несколько ключевых аспектов:
Особенно чувствительной является логика сравнения дат без времени:
if (isSameDay(date, new Date())) {
// выделение текущего дня
}
Любое смещение системного времени делает такие проверки непредсказуемыми.
Мокирование времени заключается в подмене глобального источника даты на фиксированное значение. Это позволяет стабилизировать:
Основные стратегии:
DateПростейший способ контроля времени — замена глобального конструктора:
const REAL_DATE = Date;
global.Date = class extends Date {
constructor(...args) {
if (args.length) {
return new REAL_DATE(...args);
}
return new REAL_DATE('2024-06-15T12:00:00Z');
}
static now() {
return new REAL_DATE('2024-06-15T12:00:00Z').getTime();
}
};
Такой подход обеспечивает стабильное «сегодня», но имеет ограничения:
Более устойчивый подход реализуется через системные таймеры тестового фреймворка:
jest.useFakeTimers();
jest.setSystemTime(new Date('2024-06-15T12:00:00Z'));
После фиксации времени Pikaday начинает работать в полностью детерминированной среде.
Ключевые эффекты:
new Date() возвращает зафиксированное значениеDate.now() стабиленДополнительно:
jest.useRealTimers();
восстанавливает реальное время после теста.
При использовании Sinon применяется аналогичный механизм:
import sinon from 'sinon';
const clock = sinon.useFakeTimers(new Date('2024-06-15T12:00:00Z'));
// тестирование Pikaday
clock.restore();
Sinon предоставляет более тонкий контроль:
Даже при фиксации даты остаётся проблема часового пояса. Pikaday оперирует локальными датами браузера, поэтому окружение влияет на итоговый результат.
Для стабилизации применяются:
TZ=UTC
или
process.env.TZ = 'UTC';
Это устраняет расхождения при сравнении дат.
При сравнении:
2024-06-15T00:00:00Zможет возникать сдвиг на сутки, что критично для логики «today».
Pikaday не хранит внутренний источник времени, а всегда опирается на
Date. Это создаёт прямую зависимость от глобального
окружения.
Особенно важно учитывать:
Пример чувствительного участка:
var today = new Date();
var isToday = day.getDate() === today.getDate() &&
day.getMonth() === today.getMonth() &&
day.getFullYear() === today.getFullYear();
При отсутствии мокирования такие проверки нестабильны.
Фиксированное время выбирается с учётом границ месяца:
Пример стабилизированной среды:
jest.useFakeTimers();
jest.setSystemTime(new Date('2024-03-10T10:00:00Z'));
Это позволяет воспроизводить календарную сетку без вариаций.
Мокирование времени критично при проверке ограничений:
new Pikaday({
field: document.querySelector('input'),
minDate: new Date('2024-03-05'),
maxDate: new Date('2024-03-20')
});
При нестабильном системном времени возможны ошибки:
Фиксация времени устраняет зависимость от текущей даты среды.
При тестировании Pikaday через DOM (например, JSDOM) важно учитывать:
Комбинация:
создаёт полностью воспроизводимую среду для проверки календарной логики.
Отсутствие фиксации времени приводит к следующим классам ошибок:
Особенно критично это проявляется при проверке UI-снимков календаря, где даже один день смещения меняет всю структуру сетки.
На практике применяется дополнительный уровень абстракции — обёртка над инициализацией:
function createCalendar(root, dateProvider) {
return new Pikaday({
field: root,
defaultDate: dateProvider(),
setDefaultDate: true
});
}
При тестировании dateProvider заменяется на
фиксированную функцию:
() => new Date('2024-06-15T00:00:00Z')
Это снижает зависимость от глобальных моков и упрощает контроль поведения.
Календарные алгоритмы Pikaday строятся вокруг:
Любое изменение «сегодня» влияет на:
Фиксированное время обеспечивает неизменность входных данных для этих вычислений.
Особый случай возникает при тестировании около полуночи:
Без мокирования тесты могут демонстрировать разные результаты в зависимости от времени запуска.
Фиксация системного времени полностью устраняет этот класс нестабильности.