Подтвердить что ты не робот

Как подделать Time.now?

Какой лучший способ установить Time.now для тестирования чувствительных к времени методов в unit test?

4b9b3361

Ответ 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 на Луну.

Источник: https://www.relishapp.com/rspec/rspec-mocks/docs

Ответ 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

Ответ 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.