Код, использующий Day.js, почти всегда опирается на текущее время, таймзоны и преобразования дат. В тестовой среде это создаёт нестабильность: результат выполнения может зависеть от момента запуска теста, локали окружения или настроек системы. Для устранения таких факторов применяются инструменты фиксации времени и изоляции временных зависимостей.
Ключевая проблема заключается в том, что выражения вида
dayjs() или Date.now() являются
недетерминированными. Любая проверка, завязанная на текущую дату,
требует стабилизации источника времени.
Большинство тестовых фреймворков предоставляют механизм подмены таймеров. В экосистеме JavaScript наиболее распространены Jest и Vitest, которые позволяют «замораживать» время.
При использовании Jest:
jest.useFakeTimers();
jest.setSystemTime(new Date('2025-01-01T00:00:00Z'));
После этого вызов:
dayjs().format()
будет возвращать стабильный результат, независимый от реального времени выполнения теста.
В Vitest используется аналогичный подход:
vi.useFakeTimers();
vi.setSystemTime(new Date('2025-01-01T00:00:00Z'));
Фиксация времени особенно критична при тестировании:
Некоторые окружения или библиотеки могут игнорировать fake timers,
опираясь напрямую на Date. В таких случаях применяется
явная подмена:
const RealDate = Date;
global.Date = class extends RealDate {
constructor(...args) {
if (args.length) {
return new RealDate(...args);
}
return new RealDate('2025-01-01T00:00:00Z');
}
};
global.Date.now = () => new RealDate('2025-01-01T00:00:00Z').getTime();
Такой подход обеспечивает консистентность между Date и
Day.js, поскольку Day.js внутри использует стандартный
Date.
После тестов важно восстановление:
global.Date = RealDate;
Day.js использует плагины для работы с часовыми поясами, например
utc и timezone. При тестировании важно
учитывать, что результат форматирования может зависеть от окружения.
Подключение плагинов:
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
dayjs.extend(utc);
dayjs.extend(timezone);
В тестовой среде фиксируется таймзона:
dayjs.tz.setDefault('UTC');
Это исключает влияние локальной системы, где тесты могут выполняться в разных регионах или CI-окружениях.
Одно из ключевых свойств Day.js — неизменяемость объектов. Каждый вызов метода возвращает новый экземпляр.
Проверка корректности поведения:
const base = dayjs('2025-01-01');
const result = base.add(1, 'day');
expect(base.format('YYYY-MM-DD')).toBe('2025-01-01');
expect(result.format('YYYY-MM-DD')).toBe('2025-01-02');
Такие тесты защищают от регрессий, связанных с мутациями, особенно при кастомных обёртках над Day.js.
При тестировании сервисов, использующих Day.js, часто применяется абстракция над временем:
class TimeService {
now() {
return dayjs();
}
}
В тестах этот слой заменяется:
const mockTimeService = {
now: () => dayjs('2025-01-01T00:00:00Z')
};
Такой подход снижает связанность тестов с глобальным состоянием и
исключает необходимость мокать глобальный Date.
Day.js часто используется для генерации строковых представлений времени. В таких случаях применяются snapshot-тесты:
const formatted = dayjs('2025-01-01T12:00:00Z')
.tz('UTC')
.format('YYYY-MM-DD HH:mm:ss');
expect(formatted).toMatchSnapshot();
Snapshot фиксирует формат вывода, что удобно для UI-компонентов и отчётных систем. Однако при этом важно, чтобы входное время было строго детерминировано.
Особое внимание уделяется граничным значениям:
Пример проверки перехода через день:
const date = dayjs('2025-03-31T23:59:59');
const next = date.add(1, 'second');
expect(next.format('YYYY-MM-DD')).toBe('2025-04-01');
Такие сценарии часто выявляют ошибки в логике округления и форматирования.
Day.js поддерживает парсинг различных форматов, но в тестах важно фиксировать ожидаемое поведение:
const date = dayjs('01/02/2025', 'MM/DD/YYYY');
expect(date.format('YYYY-MM-DD')).toBe('2025-01-02');
Тесты парсинга особенно важны при работе с пользовательским вводом, где формат может быть неоднозначным.
Помимо Jest и Vitest, часто используется Sinon для управления временем:
import sinon from 'sinon';
const clock = sinon.useFakeTimers(new Date('2025-01-01'));
dayjs().format();
clock.restore();
Sinon предоставляет более тонкий контроль над тиканием времени, включая ручное продвижение часов:
clock.tick(1000);
Это полезно при тестировании таймеров и отложенных операций.
При передаче дат через API Day.js часто преобразуется в ISO-строки:
const payload = {
date: dayjs('2025-01-01').toISOString()
};
expect(payload.date).toBe('2025-01-01T00:00:00.000Z');
Такие тесты предотвращают ошибки, связанные с локальными форматами или потерей точности.
При работе с UTC и локальными зонами важно тестировать оба представления:
const local = dayjs('2025-01-01T00:00:00Z').local();
const utc = dayjs('2025-01-01T00:00:00Z').utc();
expect(utc.hour()).toBe(0);
Это позволяет выявлять ошибки, связанные с двойным преобразованием или неверной интерпретацией временных меток.
Каждый тест, использующий Day.js и время, требует восстановления состояния:
afterEach(() => {
jest.useRealTimers();
});
или:
afterEach(() => {
vi.useRealTimers();
});
Без этого возможны каскадные эффекты, когда один тест влияет на другой через глобальное состояние времени.
Day.js расширяется плагинами, которые также требуют тестирования:
dayjs.extend(customPlugin);
const result = dayjs().customMethod();
expect(result).toBeDefined();
Особое внимание уделяется корректности интеграции с core API и отсутствию конфликтов с другими расширениями.