Timecop: Ruby Gem для написания тестов, чувствительных ко времени
6 марта 2024 г.Время — огромная тема. Я имею в виду, с начала времен. Наш вид думает о времени еще тысячелетия назад. Мы не знаем точную дату, когда это произошло, потому что не принимали во внимание время, пока не задумались об этом. Таким образом, мы могли иногда упускать из виду некоторые нюансы времени. И это случилось с командой, которая написала несколько тестов в проекте Rails.
Давным-давно существовал Ruby-проект, посвященный времени…
Я начал работать над этим проектом, взяв на себя роль технического руководителя. Я проводил больше проверок кода, чем кодирования. И одна из первых вещей, которые бросились мне в глаза при просмотре запросов на включение, заключалась в том, что команда меняла тесты, даже если это изменение было рефакторингом.
«Почему вы переписываете тесты для рефакторинга? При рефакторинге тесты должны оставаться прежними. Это говорит нам о том, что рефакторинг кода работает так же, как и старый код! — добавил я в комментариях.
Разочарованные члены команды отметили, что каждую неделю как минимум один тест оказывается неудачным, поскольку это зависит от даты. Один контроллер приложения получает результаты о людях, у которых день рождения приходится на эту неделю. Если они запускают набор тестов на другой неделе, им необходимо обновить ожидаемый результат.
Я был потрясен! Я планировал добавить задачу по рефакторингу этих тестов, пока владелец продукта не сказал: «Нет! В этом отношении нам необходимо создавать истории, которые повышают ценность для конечного пользователя. Тесты важны, но они не приносят пользы конечному пользователю!»
Мне не хотелось спорить, поэтому я стоически принял нелепую реальность. Я продолжал выполнять проверки кода и научился игнорировать изменения в тестах.
Но через некоторое время я нашел время заняться этими позорными тестами. И я нашел гем «timecop», гем, созданный Джоном Трупиано и поддерживаемый Трэвис Джеффри.
Что делает Timecop?
Timecop — это рубиновый драгоценный камень, созданный для тех конкретных случаев, когда у нас есть тесты, зависящие от времени. Это могут быть дни рождения, а также дни истечения срока действия токенов JWT или периодических счетов.
Timecop создает макет Time.now, Date.today и DateTime.now в одной строке. Как подчеркивают создатели Timecop в своем файле README, у гема нет зависимостей, поэтому он хорошо работает с любой тестовой средой Ruby.
Всего лишь два основных метода (Time.travel
и Time.freeze
) позволяют легко контролировать время!
Как установить Timecop
Timecop — это рубиновый драгоценный камень, доступный на Rubygems.org. Итак, вы можете просто добавить Timecop в свой Gemfile и после этого запустить bundle install
. Если вы не используете Timecop для чего-то другого, кроме тестирования, добавьте драгоценный камень в тестовую группу.
Gemfile
...
group :test do
... # Other gems
gem 'timecop'
end
Альтернативно запустите bundle add timecop
или установите его на свой компьютер с помощью gem install timecop
.
В Timecop.travel или в Timecop.freeze?
Как я уже говорил выше, вам нужно знать только два метода использования Timecop: Timecop.travel и Timecop.freeze. Но в чем разница между этими двумя методами?
Метод Timecop.travel
С помощью Timecop.travel
вы устанавливаете время на определенную дату, после чего время будет перемещаться вперед.
Например, если вы Timecop.travel
до 2 февраля 1993 г., 6:00:00, через десять секунд Time.now
вернет 2 февраля 1993 г., 6: 00:10
groundhog_day = Time.local(1993, 2, 2, 6, 0, 0)
Timecop.travel(groundhog_day)
sleep(10)
groundhog_day == Time.now # ==> false
Метод Timecop.freeze
С другой стороны, с помощью Timecop.freeze
время полностью останавливается. Итак, используя аналогичный пример, Time.now
вернет ровно 2 февраля 1993 года, 6:00:00:
groundhog_day = Time.local(1993, 2, 2, 6, 0, 0)
Timecop.travel(groundhog_day)
sleep(10)
groundhog_day == Time.now # ==> true
Другие полезные методы
Другие методы Timecop позволяют быстрее перемещать время или возвращаться к системной дате.
Timecop.scale
Двигайтесь в разы быстрее. Это хороший вариант для интеграционных тестов, когда вы хотите проверить выполнение некоторых заданий. Пример кода:
# seconds will now seem like hours
Timecop.scale(3600)
Time.now
# => 2012-09-20 21:23:25 -0500
# seconds later, hours have passed and it's gone from 9pm at night to 6am in the morning
Time.now
# => 2012-09-21 06:22:59 -0500
Timecop.return
Снова переместите время на системную дату.
GROUNDHOG_DAY = Time.local(1993, 2, 2, 23, 59, 51)
def groundhog_day? = Time.now == GROUNDHOG_DAY
Timecop.freeze(GROUNDHOG_DAY)
sleep(10)
groundhog_day? # => True
Timecop.return
groundhog_day? #=> False
Установите время выполнения тестов
Информационный файл Timecop очень понятен в использовании. Для использования Timecop в тесте, чувствительном ко времени:
joe = User.find(1)
joe.purchase_home()
assert !joe.mortgage_due?
# move ahead a month and assert that the mortgage is due
Timecop.freeze(Date.today + 30) do
assert joe.mortgage_due?
end
Для использования в группе тестов вы пишете:
describe "some set of tests to mock" do
before do
Timecop.freeze(Time.local(1990))
end
after do
Timecop.return
end
it "should do blah blah blah" do
end
end
Установите время начала тестовой среды для вашего проекта Rails
Если вы хотите, чтобы ваша тестовая среда всегда начиналась в одну и ту же дату, добавьте следующую конфигурацию:
config/environment/test.rb
... # your configuration
config.after_initialize do
# Set Time.now to February 2, 1993 06:00:00 AM (at this instant), but allow it to move forward
# year, month, day, hour, min, sec
t = Time.local(1993, 2, 2, 6, 0, 0)
Timecop.travel(t)
end
...
end
После этого каждый раз, когда вы будете запускать тест, дата будет начинаться 2 февраля в 6:00 утра.
Вот и все! Надеюсь, Timecop оказался для вас таким же полезным, как и этот пост.
Оригинал