Тестирование дат и часовых поясов с помощью Jest
Без установки глобальной переменной среды TZ
и возможности подделки текущего системного времени для обеспечения стабильности тестов
Предисловие
Уже есть много сообщений, посвященных тестированию дат и часовых поясов в JavaScript, но все те, с которыми я столкнулся, были либо слишком простыми, либо слишком грубыми (запуск всех тестов с переменной окружения TZ
), либо не касались имитации обоих дата и часовой пояс.
Даты — это сложно, а часовые пояса — сложнее, особенно когда это делается на JavaScript. Есть несколько библиотек для обхода трудностей использования Date
и предложение заменить его на что-то более современное.
Их тестирование также может быть затруднено, так как вы можете захотеть протестировать пограничные случаи (например, високосный год) или несколько часовых поясов. Большинство решений этой проблемы в экосистеме JavaScript фокусируются на одной из трех вещей:
- Подделка системного времени и возможность вручную управлять им и запускать таймеры (например,
setTimeout
,setInterval
и т. д.). Не дожидаясь фактического указанного времени, чтобы пройти. - «Блокировка» системного времени до некоторого заранее определенного значения, чтобы вы могли выполнять на нем свои тесты. Например, если вы пишете средство выбора даты, вы можете захотеть «привязать» время к заданной дате, а затем убедиться, что средство выбора ведет себя так, как ожидалось, в это время. В противном случае о ваших
expect
становится сложнее рассуждать, поскольку они динамичны по своей природе. - Изменение часового пояса системы, чтобы вы могли проверить, как ваш код ведет себя в разных средах. Это особенно актуально для веб-приложений, где вы не можете контролировать среду, в которой выполняется код, поскольку он выполняется на компьютерах пользователей.
У каждой из этих проблем есть несколько изолированных решений в виде пакетов npm или способов запуска ваших тестов:
Ручное управление таймерами
Jest имеет довольно хорошие встроенные мокаторы таймера, и фальшивые таймеры Sinon.js тоже очень хороши (и, по моему опыту, проще в использовании).
См. их документы для примеров, там есть что охватить, и они отлично справляются с этим!
Блокировка системного времени
Для блокировки системного времени вы можете использовать mockdate
, который позволяет указать дату-время в тесте, после чего последующие использования Date
будут использовать это время:
Подделка часового пояса
Есть два основных подхода к решению вопроса подделки часового пояса в тестах:
- Установите переменную окружения
TZ
перед запуском Jest:
TZ=UTC jest
Это приведет к тому, что все тесты будут выполняться в часовом поясе UTC. Независимо от того, связан ли тест с часовым поясом или нет. Это еще большая проблема, если вы хотите протестировать несколько часовых поясов. Вам придется прибегнуть к таким решениям, как:
TZ=UTC jest && TZ=America/New_York jest
(или запускать их одновременно, используя что-то вроде concurrently
), но опять же — даже если только несколько ваших тестов требуют тестирования часового пояса, вы запускаете их все для всех часовых поясов, а также сталкиваетесь с тем, что тестовый код не знает часового пояса, в котором он работает.
2. Используйте такой пакет, как timezoned-date
, который позволяет создать новый конструктор Date
с динамическим offset
:
это прекрасно работает, просто не забудьте сохранить исходный Date
перед его переопределением и восстановить его позже:
Блокировка системного времени И установка часового пояса
Совместное использование mockdate
и timezoned-date
создает проблему, которая может быть неочевидной — поскольку оба (прямо или косвенно) переопределяют глобальный Date
, важна реализация — особенно в случае mockdate
, который переопределяет сброс самого исходного Date
. На момент написания этой статьи реализация сохраняет Date
при первом require
-ed модуля:
// mockdate.ts const RealDate = Date;
boblauer/MockDate
Объект JavaScript Fock Date, который можно использовать для изменения «сейчас. - boblauer/MockDategithub.com»
Это означает, что этот код не будет вести себя так, как ожидалось:
Как видите, mockdate
работал нормально, но не учитывал Date
конструктор, который мы только что создали и переопределили с помощью timezoned-date
.
Чтобы исправить это, мы можем создать тестовую служебную функцию, которая решает проблему, абстрагируя проблемную реализацию, а также упрощает тестовый код:
Тогда использование довольно простое:
Заключение
Я надеюсь, что этот пост помог вам лучше понять различные способы проверки дат в JavaScript, особенно когда вы используете сторонние модули, которые полагаются на глобальные переменные и не внедряют свои зависимости (например, используя Date
).
Реализация setupMockDate
, представленная выше, должна быть достаточно общей, чтобы охватывать различные варианты использования, и может быть полезной, если ее опубликовать напрямую в npm, чтобы избавить вас от необходимости копировать и вставлять это в свою кодовую базу. Если я опубликую это, я обязательно обновлю этот пост в блоге.