⛳
Rails の travel_to で秒未満も固定する
特定のテストで『テスト時はこの時刻で固定する』みたいな検証をしたいことがたまにあります。
そういうときに ActiveSupport::Testing::TimeHelpers
の travel_to
等が利用できます。
travel_to
は『ブロック内の時間を引数の時間で固定する』という機能になります。
freeze_time = Time.parse("2025-03-14 12:34:56")
travel_to(freeze_time) do
# このブロック内では常に freeze_time の時刻になる
pp Time.now # => 2025-03-14 12:34:56 +0900
sleep 3
# 3秒経っていてたとしても同じ時刻が返ってくる
pp Time.now # => 2025-03-14 12:34:56 +0900
end
基本的なテストをする場合はこれで問題ないと思います。
秒未満まで固定できない
travel_to
ではデフォルトでは秒未満の数値までは固定されません。
なので厳密な時刻を扱う世界では意図するテストにならないケースがあります。
# Time.now は秒未満まで含まれている
freeze_time = Time.now
pp freeze_time
# => 2025-02-21 20:09:29.639692144 +0900
travel_to(freeze_time) do
# freeze_time は秒未満の数値まで含まれているが秒単位までしか固定されない
# なので Time.now は travel_to を呼び出すよりも『過去の時刻』になってしまうケースがある
pp Time.now
# => 2025-02-21 20:09:29 +0900
end
このように 未来の時刻 -> 過去の時刻
に遡ってしまうケースがありえてしまいます。
with_usec:
を利用する
Rails 7.1 から追加された with_usec:
オプションを利用すると固定される時刻に『秒未満』の数値も含まれるようになります。
# Time.now は秒未満まで含まれている
freeze_time = Time.now
pp freeze_time
# => 2025-02-21 20:13:50.86102677 +0900
# with_usec: true で秒未満も固定されうようになる
travel_to(freeze_time, with_usec: true) do
pp Time.now
# => 2025-02-21 20:13:50.86102677 +0900
end
これで変に 未来 -> 過去
に時間を遡ることもなくなりますね。
Discussion