Какой лучший способ установить Time.now
для тестирования чувствительных к времени методов в unit test?
Как подделать Time.now?
Ответ 1
Мне очень нравится библиотека 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
У меня есть cucumber помощники для 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"
Ответ 2
Лично я предпочитаю делать тактируемые инъекции, например:
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))
Ответ 3
Я использую RSpec, и я сделал это: Time.stub! (: now).and_return (2.days.ago), прежде чем позвонить в Time.now. Таким образом, я могу контролировать время, которое я использовал для этого конкретного тестового примера.
Ответ 4
Сделайте time-warp
time-warp - это библиотека, которая делает то, что вы хотите. Он дает вам метод, который занимает время и блок, и все, что происходит в блоке, использует фальшивое время.
pretend_now_is(2000,"jan",1,0) do
Time.now
end
Ответ 5
Используя Rspec 3.2, я нашел единственный простой способ подделать возвращаемое значение Time.now:
now = Time.parse("1969-07-20 20:17:40")
allow(Time).to receive(:now) { now }
Теперь Time.now всегда будет возвращать дату посадки Аполлона-11 на Луну.
Ответ 6
Не забывайте, что Time
- это просто константа, которая ссылается на объект класса. Если вы хотите вызвать предупреждение, вы всегда можете сделать
real_time_class = Time
Time = FakeTimeClass
# run test
Time = real_time_class
Ответ 7
Также см. этот вопрос, где я также помещаю этот комментарий.
В зависимости от того, с чем вы сравниваете Time.now
, иногда вы можете менять свои светильники для достижения той же цели или тестирования той же функции. Например, у меня была ситуация, когда мне нужно было одно дело, если какая-то дата была в будущем, а другая - в прошлом. То, что я смог сделать, это включить в мои приборы встроенный рубин (erb):
future:
comparing_date: <%= Time.now + 10.years %>
...
past:
comparing_date: <%= Time.now - 10.years %>
...
Затем в ваших тестах вы выбираете, какой из них следует использовать для тестирования различных функций или действий на основе времени относительно Time.now
.
Ответ 8
Если бы та же проблема, я должен был подделать время для спецификации в течение определенного дня и времени, просто сделал это:
Time.stub!(:now).and_return(Time.mktime(2014,10,22,5,35,28))
это даст вам:
2014-10-22 05:35:28 -0700
Ответ 9
Если у вас включен ActiveSupport, вы можете использовать:
travel_to Time.zone.parse('2010-07-05 08:00')
http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html
Ответ 10
Этот вид работ и позволяет вложенность:
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
Ответ 11
В зависимости от того, с чем вы сравниваете Time.now
, иногда вы можете менять свои светильники для достижения той же цели или тестирования той же функции. Например, у меня была ситуация, когда мне нужно было одно дело, если какая-то дата была в будущем, а другая - в прошлом. То, что я смог сделать, это включить в мои приборы встроенный рубин (erb):
future:
comparing_date: <%= Time.now + 10.years %>
...
past:
comparing_date: <%= Time.now - 10.years %>
...
Затем в ваших тестах вы выбираете, какой из них следует использовать для тестирования различных функций или действий на основе времени относительно Time.now
.
Ответ 12
Я просто имею это в своем тестовом файле:
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 перезаписывается, чтобы использовать то, что вы когда-либо захотите.
Ответ 13
Я всегда извлекаю Time.now
в отдельный метод, который я превращаю в attr_accessor
в макет.
Ответ 14
Недавно выпущенный 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-клиент, но он решает поднять это исключение вместо того, чтобы вернуть мне строку?" без фактической настройки сетевых условий, которые приводят к этому исключению (что может быть сложно). Он также позволяет вам проверять аргументы на функции redef'd.