ActiveSupport::TestCase#travel_to
ActiveSupport::TestCase中可以使用travel_to指定测试日期,令block中的代码的“当前时间”变成指定日期
class GoodPriceTest < ActiveSupport::TestCase
test 'xxxxx' do
travel_to Time.zone.parse('2017-12-16 15:19:39') do
# assert_equal ...
end
end
end
其源码如下。主要是给Time、Date重定义now、today方法(Time.current也是调用now),使它们总是返回指定时间,然后执行block,最后恢复now、today方法。
此外还有travel可传时间段,指定“XX天前”,“XX小时后”。
如果block中要依赖两次调用now来计算执行时间,是做不到的,因为“总是返回指定时间”。
# activesupport-4.1.4/lib/active_support/testing/time_helpers.rb
module ActiveSupport
module Testing
class SimpleStubs
Stub = Struct.new(:object, :method_name, :original_method)
def initialize
@stubs = {}
end
def stub_object(object, method_name, return_value)
key = [object.object_id, method_name]
if stub = @stubs[key]
unstub_object(stub)
end
new_name = "__simple_stub__#{method_name}"
@stubs[key] = Stub.new(object, method_name, new_name)
object.singleton_class.send :alias_method, new_name, method_name
object.define_singleton_method(method_name) { return_value }
end
def unstub_all!
@stubs.each_value do |stub|
unstub_object(stub)
end
@stubs = {}
end
private
def unstub_object(stub)
singleton_class = stub.object.singleton_class
singleton_class.send :undef_method, stub.method_name
singleton_class.send :alias_method, stub.method_name, stub.original_method
singleton_class.send :undef_method, stub.original_method
end
end
module TimeHelpers
def travel(duration, &block)
travel_to Time.now + duration, &block
end
def travel_to(date_or_time, &block)
if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
now = date_or_time.midnight.to_time
else
now = date_or_time.to_time
end
simple_stubs.stub_object(Time, :now, now)
simple_stubs.stub_object(Date, :today, now.to_date)
if block_given?
begin
block.call
ensure
travel_back
end
end
end
def travel_back
simple_stubs.unstub_all!
end
private
def simple_stubs
@simple_stubs ||= SimpleStubs.new
end
end
end
end