ZEALOTエンジニアブログ

Rubyで時間に依存した機能をテストする時便利なgem, Timecop

Pocket

アプリケーションの機能としてある日時になったら「なになに」する、みたいなものはよくあると思います。例えば、最後にアクセスした日から1ヶ月以上過ぎているユーザにメールを送信する、とか。
こういう機能をテストする場合、どうやってテストしようか色々考えると思います。(DBに登録されている時刻を更新したり、対象の関数に日時を渡せるようにしたり)

そこで、今回紹介するtimecopというgemを使うと今回のようなテストも簡単に行えます。主に以下の2つの関数を使って時間を操作します。

  • Timecop.freeze – 指定した時刻に固定する
  • Timecop.travel – 指定した時刻に移動し、その時点から時間が流れる

サンプルを見たほうが早いと思うので、簡単にPadrinoのアプリケーションをrspecでテストしてみたいと思います。

Padrinoのプロジェクトを作成(rspecを使うように指定)

app/app.rbにテスト対象のコードを記述

/currentにアクセスすると現在時刻を返す簡単なものを作成します。

spec/app/app_spec.rb(テストコード)を作成

大きく分けて、Timecopを使わない場合、Timecop.freezeを使った場合、Timecop.travelを使った場合の3つのテストを書いてみます。各コンテキストの最初に1回だけTimecop.freeze, Timecop.travelを実行し時間を操作しています。各exampleでは1秒間のスリープを入れています。(因みにこのサンプルではミリ秒単位のことは考えていないので、場合によってはテストがコケるかも知れません)

テストを実行

この結果を見るとわかると思いますが、Timecop.freezeは指定した時刻で時間を固定します。freezeしたあとのどのタイミングでTime.nowをしてみてもfreezeした時の時刻が取得できると思います。
Timecop.travelの場合はといいますと、指定した時刻から時間が流れている感じになりますので、1秒後にTime.nowとしますと指定した時刻+1秒後の時刻が取得できると思います。
afterで実行しているTimecop.returnは時刻を元に戻すためですので、テストの終わりに必ず実行するようにしておくといいでしょう。

また、Time.now以外にもDate.todayDateTime.nowも同じように偽装されますので、それらを利用している関数のテストにも使うことが出来ます。

ということで、Timecopを使うとテストがシンプルに書けそうですね。

Pocket