単体テストで時間依存メソッドをテストする目的でTime.now
を設定する最良の方法は何ですか?
Timecop ライブラリが本当に好きです。タイムワープはブロック形式で実行できます(タイムワープと同様)。
Timecop.travel(6.days.ago) do
@model = TimeSensitiveMode.new
end
assert @model.times_up!
(はい、ブロック形式のタイムトラベルをネストできます。)
また、宣言的なタイムトラベルを行うこともできます。
class MyTest < Test::Unit::TestCase
def setup
Timecop.travel(...)
end
def teardown
Timecop.return
end
end
私はいくつかの キュウリ Timecopのヘルパーを持っています ここ 。彼らはあなたに次のようなことをさせます:
Given it is currently January 24, 2008
And I go to the new post page
And I fill in "title" with "An old post"
And I fill in "body" with "..."
And I press "Submit"
And we jump in our Delorean and return to the present
When I go to the home page
I should not see "An old post"
個人的には、次のようにクロックを注入可能にすることを好みます:
def hello(clock=Time)
puts "the time is now: #{clock.now}"
end
または:
class MyClass
attr_writer :clock
def initialize
@clock = Time
end
def hello
puts "the time is now: #{@clock.now}"
end
end
ただし、多くの人はモック/スタブライブラリを使用することを好みます。 RSpec/flexmockでは、次を使用できます。
Time.stub!(:now).and_return(Time.mktime(1970,1,1))
またはモカで:
Time.stubs(:now).returns(Time.mktime(1970,1,1))
私はRSpecを使用していますが、これを行いました:Time.stub!(:now).and_return(2.days.ago) Time.nowを呼び出す前に。そのようにして、特定のテストケースに使用した時間を制御できます。
time-warp を実行します
タイムワープは、あなたが望むことをするライブラリです。それはあなたに時間とブロックを要する方法を与え、ブロックで起こることはすべて偽造された時間を使用します。
pretend_now_is(2000,"jan",1,0) do
Time.now
end
Rspec 3.2を使用して、Time.nowの戻り値を偽造するために見つけた唯一の簡単な方法は次のとおりです:
now = Time.parse("1969-07-20 20:17:40")
allow(Time).to receive(:now) { now }
現在、Time.nowは、アポロ11号が月面に着陸した日付を常に返します。
Time
は単にクラスオブジェクトを参照する定数であることを忘れないでください。警告を発生させたい場合は、いつでも行うことができます
real_time_class = Time
Time = FakeTimeClass
# run test
Time = real_time_class
this question も参照してください。ここでもこのコメントを掲載しています。
Time.now
と比較する対象によっては、フィクスチャを変更して同じ目標を達成したり、同じ機能をテストしたりできる場合があります。たとえば、ある日付が未来の場合に発生することと、過去の場合に発生することの両方が必要な状況がありました。私ができることは、フィクスチャに埋め込みのRuby(erb):
future:
comparing_date: <%= Time.now + 10.years %>
...
past:
comparing_date: <%= Time.now - 10.years %>
...
次に、テストで、Time.now
に関連する時間に基づいて、異なる機能またはアクションをテストするために使用するものを選択します。
ActiveSupportが含まれている場合は、次を使用できます。
travel_to Time.zone.parse('2010-07-05 08:00')
http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html
同じ問題を抱えていたので、特定の日のために仕様の時間を偽造しなければなりませんでした。
Time.stub!(:now).and_return(Time.mktime(2014,10,22,5,35,28))
これはあなたに与えます:
2014-10-22 05:35:28 -0700
この種の作品とネストを可能にします:
class Time
class << self
attr_accessor :stack, :depth
end
def self.warp(time)
Time.stack ||= []
Time.depth ||= -1
Time.depth += 1
Time.stack.Push time
if Time.depth == 0
class << self
alias_method :real_now, :now
alias_method :real_new, :new
define_method :now do
stack[depth]
end
define_method :new do
now
end
end
end
yield
Time.depth -= 1
Time.stack.pop
class << self
if Time.depth < 0
alias_method :new, :real_new
alias_method :now, :real_now
remove_method :real_new
remove_method :real_now
end
end
end
end
最後にスタックおよび深さアクセサの定義を解除することにより、わずかに改善できます。
使用法:
time1 = 2.days.ago
time2 = 5.months.ago
Time.warp(time1) do
Time.real_now.should_not == Time.now
Time.now.should == time1
Time.warp(time2) do
Time.now.should == time2
end
Time.now.should == time1
end
Time.now.should_not == time1
Time.now.should_not be_nil
私は私のテストファイルにこれを持っています:
def time_right_now
current_time = Time.parse("07/09/10 14:20")
current_time = convert_time_to_utc(current_date)
return current_time
end
私のTime_helper.rbファイルには
def time_right_now
current_time= Time.new
return current_time
end
そのため、time_right_nowをテストするときは、必要なときに使用するように上書きされます。
私は常にTime.now
を別のメソッドに抽出し、それをモックでattr_accessor
にします。
最近リリースされた Test::Redef
は、依存関係注入スタイルでコードを再構築しなくても、このおよびその他のfakeryを簡単にします(特に、他の人々のコード。)
fake_time = Time.at(12345) # ~3:30pm UTC Jan 1 1970
Test::Redef.rd 'Time.now' => proc { fake_time } do
assert_equal 12345, Time.now.to_i
end
ただし、これが偽造されない時間を取得する他の方法に注意してください(Date.new
、独自のシステムコールを行うコンパイル済み拡張機能、現在のタイムスタンプなどを知っている外部データベースサーバーなどへのインターフェイス。上記のTimecopライブラリがこれらの制限を克服するようです。
他の優れた用途には、「このフレンドリーなhttpクライアントを使用しようとしているが、文字列を返さずに例外を発生させるとどうなるか」などのテストが含まれます。その例外につながるネットワーク条件を実際に設定することはありません(注意が必要な場合があります)。また、再定義された関数の引数を確認できます。
Time.now
と比較する対象によっては、フィクスチャを変更して同じ目標を達成したり、同じ機能をテストしたりできる場合があります。たとえば、ある日付が未来の場合に発生することと、過去の場合に発生することの両方が必要な状況がありました。私ができることは、フィクスチャに埋め込みのRuby(erb):
future:
comparing_date: <%= Time.now + 10.years %>
...
past:
comparing_date: <%= Time.now - 10.years %>
...
次に、テストで、Time.now
に関連する時間に基づいて、異なる機能またはアクションをテストするために使用するものを選択します。