読者です 読者をやめる 読者になる 読者になる

Rails Webook

自社のECを開発している会社で働いています。Rails情報やサービスを成長させる方法を書いていきます

RailsでTimecopを使って日付/時間のテストをする

Rails初級 Rails ActiveSupport

TimecopというGemを使うことで、好きな時刻に移動(Timecop.travel)させたり、時刻を止めたり(Timecop.freeze)といったことができ、日付/時間に関するテストをやるときに必須といっていいほど便利なGemです。
具体的な「日付/時間が関係するテスト例」として、うるう歳のテスト、税率変更のテスト、有効期限のテストなどがあります。


動作確認

  • Rails 4.1.7
  • Timecop 0.7.1

目次

  1. Timecopのインストール方法
  2. 時間の移動方法(travel)
  3. 時間の停止方法(freeze)
  4. 時間の進みを加速させる(scale)


Timecopのインストール方法

Gemfiletimecopを追加します。

# Gemfile
group :test do
  gem 'timecop'
end


Bundlerを実行します。

bundle install


時間の移動方法(travel)

Timcecop.travelは、Timecop.returnが呼ばれるまで、「指定した時間に移動させておく」ことができます。

# 現在の日付
# 2014-12-17 12:30:30 UTC

# 10日前の日付に移動する
Timecop.travel(10.days.ago)

# 10日前の日付になる
puts Time.zone.now #=> 2014-12-07 12:30:30 UTC

# テストしたい処理を記載する ...

# 時間が戻る
Timecop.return

# 現在の日付に戻っている
#=> 2014-12-17 12:30:30 UTC


また、Timcecop.travelにブロック引数を指定することで、ブロック内のみで指定した時間に移動します。

# 現在の日付
# 2014-12-17 12:30:30 UTC

# ブロック内のみ指定した日付(1日後に移動する)
Timecop.travel(1.day.from_now) do
  # 1日後の日付になる
  puts Time.zone.now #=> 2014-12-18 12:30:30 UTC
end

# ブロックを抜けると時間が戻っている
# 2014-12-17 12:30:30 UTC


時間の停止方法(freeze)

Timcecop.freezeは、Timecop.returnが呼ばれるまで、「指定した時間に停止させておく」ことができます。

# 10日前の日付で時間を止める
Timecop.freeze(10.days.ago)

# 10日前の日付になる
puts Time.zone.now #=> 2014-12-07 12:37:30 UTC

sleep 10 # 10秒経過させる

# 10秒経過させたが同じ秒数。時間が止まっている
puts Time.zone.now #=> 2014-12-07 12:37:30 UTC

# 時間が戻る
Timecop.return


また、Timcecop.freezeにブロック引数を指定することで、ブロック内の処理のみで指定した時間に停止します。

# ブロック内のみ指定した日付(1日後に移動する)
Timecop.freeze(1.day.from_now) do
  # 1日後の日付になる
  puts Time.zone.now #=> 2014-12-18 12:40:40 UTC
  sleep 10 # 10秒経過させる
  # 10秒たっても同じ時刻
  puts Time.zone.now #=> 2014-12-18 12:40:40 UTC
end

# ブロックを抜けると時間が戻っている
# 2014-12-17 12:40:50 UTC


時間の進みを加速させる(scale)

Timcecop.scale(秒数)は、Timecop.returnが呼ばれるまで、「1秒の時間を指定した時間(単位:秒)の進み」にさせることができます。
たとえば、Timecop.scale(60)なら、1秒経過すると時間が60秒進みます。

# 1秒を60秒(1分)にする
Timecop.scale(60)

#=> 2014-12-17 12:44:30 UTC
sleep 5 # scaleで1秒を1分の時間の進みにしているので、5秒経過は5分経過になる
#=> 2014-12-17 12:49:30 UTC
sleep 5 # また5分経過になる
#=> 2014-12-17 12:54:30 UTC

# 時間を戻す
Timecop.return

sleep 5
# 5秒を3回止まったので、開始時間より+15秒になっている
# 2014-12-17 12:44:45 UTC

もちろん、Timecop.travelTimecop.freezeのようにブロックも使えます。


以上です。
日付/時刻の便利なメソッドはタイムゾーンや時刻処理のまとめを参照してください。