为了在单元测试中测试时间敏感方法,设置Time.now
的最佳方法是什么?
答案 0 :(得分:74)
我非常喜欢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 cucumber提供了一些here帮助器。他们让你做的事情如下:
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"
答案 1 :(得分:42)
我个人更喜欢让时钟可注射,如下:
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))
答案 2 :(得分:14)
我正在使用RSpec,我在调用Time.now之前执行了此操作: Time.stub!(:now).and_return(2.days.ago)。通过这种方式,我能够控制我用于特定测试用例的时间
答案 3 :(得分:10)
time-warp是一个可以满足您需求的库。它为您提供了一个花费时间和块的方法,并且块中发生的任何事情都使用伪造的时间。
pretend_now_is(2000,"jan",1,0) do
Time.now
end
答案 4 :(得分:9)
使用Rspec 3.2,我发现伪造Time.now返回值的唯一简单方法是:
now = Time.parse("1969-07-20 20:17:40")
allow(Time).to receive(:now) { now }
现在Time.now将永远返回阿波罗11登陆月球的日期。
答案 5 :(得分:7)
不要忘记Time
只是一个引用类对象的常量。如果您愿意发出警告,您可以随时
real_time_class = Time
Time = FakeTimeClass
# run test
Time = real_time_class
答案 6 :(得分:1)
另请参阅this question我在哪里发表此评论。
根据您对Time.now
的比较,有时您可以更改灯具以实现相同的目标或测试相同的功能。例如,我有一种情况,即如果某个日期在未来,我需要发生一件事情,如果是在过去,则会发生另一件事。我能做的是在我的灯具中加入一些嵌入式红宝石(erb):
future:
comparing_date: <%= Time.now + 10.years %>
...
past:
comparing_date: <%= Time.now - 10.years %>
...
然后在测试中,您可以根据相对于Time.now
的时间选择使用哪一个来测试不同的功能或操作。
答案 7 :(得分:1)
如果出现同样的问题,我不得不假装某个特定日期和时间的规格时间:
Time.stub!(:now).and_return(Time.mktime(2014,10,22,5,35,28))
这会给你:
2014-10-22 05:35:28 -0700
答案 8 :(得分:1)
如果您包含ActiveSupport,则可以使用:
travel_to Time.zone.parse('2010-07-05 08:00')
http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html
答案 9 :(得分:0)
这种作品允许嵌套:
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
答案 10 :(得分:0)
根据您对Time.now
的比较,有时您可以更改灯具以实现相同的目标或测试相同的功能。例如,我有一种情况,即如果某个日期在未来,我需要发生一件事情,如果是在过去,则会发生另一件事。我能做的是在我的灯具中加入一些嵌入式红宝石(erb):
future:
comparing_date: <%= Time.now + 10.years %>
...
past:
comparing_date: <%= Time.now - 10.years %>
...
然后在测试中,您可以根据相对于Time.now
的时间选择使用哪一个来测试不同的功能或操作。
答案 11 :(得分:0)
我只是在我的测试文件中有这个:
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会被覆盖,以便随时使用它。
答案 12 :(得分:0)
我总是将Time.now
提取到一个单独的方法中,然后在模拟中转换为attr_accessor
。
答案 13 :(得分:0)
最近发布的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客户端时会发生什么,但它决定将此异常提高而不是返回一个字符串?”没有实际设置导致该异常的网络条件(这可能是棘手的)。它还允许您检查重新定义函数的参数。
答案 14 :(得分:0)
我自己的解决方案 https://github.com/igorkasyanchuk/rails_time_travel - 带有 UI 的 gem,因此您无需在代码中硬编码任何日期时间。只需从用户界面更改即可。
它可能对您的 QA 团队或在登台中测试应用程序也非常有用。