テストでタイムゾーンを考慮した

テストのプラクティスにて

Date型への変換メソッドのテストを行いました。

テストの観点

対象のメソッドは下記です。

app/models/report.rb

class Report < ApplicationRecord
  belongs_to :user
  has_many :comments, as: :commentable, dependent: :destroy

  validates :title, presence: true
  validates :content, presence: true

  def created_on
    created_at.to_date
  end

Reportモデルのメソッドで、型変換を行っています。

irb(main):014:0> Report.columns_hash['created_at'].type
=> :datetime
# これがclass Date に変換されていたらOK

注意点!

まず自分は型チェックのみの実装を行いました。

#型チェック
    assert_not_equal Date, report.created_at.class
    assert_equal Date, report.created_on.class

テストの方針にもよるでしょうが、
reportの期待値が一致するか?
つまり、インスタンス生成時のcreated_atもチェックしてみるべきなのです。

そして初期の実装がこちら。

test '#created_on' do
    me = User.create(email: 'me@example.com', password: 'password')
    report = Report.create(user_id: me.id, title: 'タイトル', content: '内容')
 
    #型チェック
    assert_not_equal Date, report.created_at.class
    assert_equal Date, report.created_on.class
 
 #期待値のチェック
 assert_not_equal Date.today, report.created_at
    assert_equal Date.today, report.created_on

実はこちらバグになりうる問題点があります。
何か分かりますか?

Date.todayしたときの注意点

Date.todayを行うと普通に今日が取得できると思いますよね?
でもこれってどこを参照してどうやってできるのか?まったく把握していませんでした。
指摘をもらったのがこのタイムゾーンについてです。

フィヨルドでも大変お世話になっている、伊藤さんのめちゃくちゃ分かりやすい記事↓

qiita.com


抜粋:

# todayは環境変数のタイムゾーンを使う。(ただし、タイムゾーン情報は保持されない)
l Date.today
=> 2015/01/01, Date

# currentはapplication.rbのタイムゾーンを使う。
l Date.current
=> 2014/12/31, Date

# Time.zone.todayはDate.current と同じ
l Time.zone.today
=> 2014/12/31, Date

環境変数タイムゾーンを使うか、またはapplication.rbのタイムゾーンを使うのか。
ここを知っておかなければ、テストは通ったのに本番環境でバグが起こるなんてことも??

まとめ

特に理由がなければ Time(RailsならTimeWithZone)を使う < 完全な受け売り
ただ今回のテスト対象のメソッドって、Dateに型変換するメソッドなので、私は分かりやすい様に、
Date.currentにて実装しました。

★Date.todayは使わない!

以上です。