Time.now
为在单元测试中测试时间敏感方法的目的而设置的最佳方法是什么?
Time.now
为在单元测试中测试时间敏感方法的目的而设置的最佳方法是什么?
Answers:
我真的很喜欢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 3.2,我发现伪造Time.now的唯一简单方法是返回值:
now = Time.parse("1969-07-20 20:17:40")
allow(Time).to receive(:now) { now }
现在,Time.now将始终返回阿波罗11号登月的日期。
如果包含ActiveSupport,则可以使用:
travel_to Time.zone.parse('2010-07-05 08:00')
http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html
也请参阅此问题,我也在此发表评论。
根据所比较Time.now
的内容,有时您可以更改灯具以实现相同的目标或测试相同的功能。例如,我遇到这样的情况,如果某个日期是将来的某个日期,那么我需要做一件事;如果是过去的某个日期,则需要另一件事。我能够做的就是在我的装置中包括一些嵌入式红宝石(erb):
future:
comparing_date: <%= Time.now + 10.years %>
...
past:
comparing_date: <%= Time.now - 10.years %>
...
然后,在测试中,您将根据相对于的时间选择使用哪一个来测试不同的功能或动作Time.now
。
这种工作并允许嵌套:
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
即使不使用依赖注入方式重新构造代码,最新发布的代码也Test::Redef
可以使此操作和其他伪造变得容易(特别是在使用其他人的代码的情况下特别有用)。
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客户端,但它决定引发此异常而不是返回一个字符串时会发生什么情况?”之类的事情。没有实际设置导致该异常的网络条件(这可能很棘手)。它还可以让您检查重新定义函数的参数。