💎

Ruby Rails 時間を操れるgem Timecopの導入と活用法

に公開

ECサイトの開発では、「購入時刻の処理が正しく動いているか?」をテストする場面が多くあります。
たとえば、以下のようなケースです。

  • 予約販売の商品が、指定の時間より前に購入できてしまわないか?
  • タイムセールの終了時刻を過ぎても割引が適用されていないか?
  • 一定期間内の購入回数をカウントするロジックが正しく機能しているか?

こうしたテストを行う際、いちいちシステム時刻を変更するのは現実的ではありません。
そこで便利なのが Timecop というgemです。

Timecopの導入方法

まず、Gemfile に以下を追加し、bundle install を実行します。

group :test do
  gem 'timecop'
end

Timecopの基本的な使い方

時刻を固定する (Timecop.freeze)

require 'timecop'

Timecop.freeze(Time.local(2025, 1, 1, 12, 0, 0)) do
  puts Time.now  # => 2025-01-01 12:00:00
end

puts Time.now  # 実際の現在時刻が表示される

過去や未来にジャンプする (Timecop.travel)

Timecop.travel(Time.local(2025, 12, 31)) do
  puts Time.now  # => 2025-12-31 00:00:00
end

時の流れを遅くする (Timecop.scale)

Timecop.scale(3600)  # 1秒が1時間に相当するスピードで時間が進む

ECの購入時刻のテストにTimecopを活用する

例えば、「午前10時~11時限定のタイムセール」 のロジックが正しく動作するかテストしたい場合、以下のように実装できます。

describe 'タイムセールの価格適用' do
  it '10:00〜10:59の間は割引価格になる' do
    Timecop.freeze(Time.local(2025, 1, 1, 10, 30)) do
      product = Product.find(1)
      expect(product.discounted_price).to eq(500) # 割引価格
    end
  end

  it '11:00以降は通常価格に戻る' do
    Timecop.freeze(Time.local(2025, 1, 1, 11, 0)) do
      product = Product.find(1)
      expect(product.discounted_price).to eq(1000) # 通常価格
    end
  end
end

Timecopを使う際の注意点

  • Timecop.freeze を使ったら、ensure ブロックで Timecop.return を呼ぶか、ブロック内で使う
  • Time.now を直接参照するコードでのみ有効(外部APIのタイムスタンプには影響なし)

まとめ

EC開発では、購入時刻に関するテストが重要になります。
Timecopを使えば、自由に時間を操りながら簡単にテストを実装できる ので、ぜひ活用してみてください!

Discussion