Код модульного тестирования, который получает текущее время - PullRequest
5 голосов
/ 03 апреля 2009

Каков наилучший способ написания модульного теста для кода, который получает текущее время? Например, некоторые объекты могут быть созданы только в рабочие дни, другие объекты учитывают текущее время при проверке разрешений на выполнение некоторых действий и т. Д.

Полагаю, мне стоит смоделировать Date.today и Time.now. Это правильный подход?

Обновление: оба решения (a) Time.is и (b) Time.stubs (: сейчас) .returns (t) работают. (а) очень хороший подход, но (б) решение будет более согласованным с другим тестовым кодом.

На этот вопрос автор просит общего решения. Для Ruby, в моем варианте два вышеупомянутых решения проще и поэтому лучше, чем извлечение кода, который получает текущую дату / время

Кстати, я рекомендую использовать Хронический , чтобы получить необходимое время, например.

require 'Chronic'    
mon = Chronic.parse("next week monday")
Time.stubs(:now).returns(mon)

Ответы [ 5 ]

6 голосов
/ 03 апреля 2009

Следующее от Мысли Джея Филда . Позволяет переопределить Time.now на время блока.

require 'time'

class Time
  def self.metaclass
    class << self; self; end
  end

  def self.is(point_in_time)
    new_time = case point_in_time
      when String then Time.parse(point_in_time)
      when Time then point_in_time
      else raise ArgumentError.new("argument should be a string or time instance")
    end
    class << self
      alias old_now now
    end
    metaclass.class_eval do
      define_method :now do
        new_time
      end
    end
    yield
    class << self
      alias now old_now
      undef old_now
    end
  end
end

Time.is(Time.now) do
  Time.now # => Tue Nov 13 19:31:46 -0500 2007
  sleep 2
  Time.now # => Tue Nov 13 19:31:46 -0500 2007
end

Time.is("10/05/2006") do
  Time.now # => Thu Oct 05 00:00:00 -0400 2006
  sleep 2
  Time.now # => Thu Oct 05 00:00:00 -0400 2006
end
3 голосов
/ 04 апреля 2009

Mocking Time.now или Date.today кажется достаточно простым, будет выглядеть примерно так:

require 'rubygems'
require 'test/unit'
require 'mocha'

class MyClass

  def foo
    Time.now
  end

end

class MyTest < Test::Unit::TestCase

  def test_foo
    assert true
    t = Time.now
    Time.expects(:now).returns(t)
    assert_equal t, MyClass.new.foo
  end

end
3 голосов
/ 03 апреля 2009

С самого начала я бы предположил, что лучшим подходом к этому было бы то, чтобы ваш объект не получал время. Другими словами, передайте дату / время в любой вызываемый метод объекта, который использует встроенные временные конструкции. В зависимости от ваших обстоятельств это может оказаться гораздо более простым решением, чем макетирование Date.today и Time.now, как вы предлагаете.

Редактировать : Я говорю это резко контрастируя с предложением иметь интерфейс ITimeProvider, который вы передаете в качестве зависимости ... это просто излишество, по моему мнению.

3 голосов
/ 03 апреля 2009

Передайте класс ITimeProvider (например) в свою подпрограмму, чтобы использовать его, чтобы получить время, затем вы можете смоделировать его и использовать объект-заглушку, чтобы всегда предоставлять вам согласованное время для использования подпрограммы.

0 голосов
/ 03 апреля 2009

Создание объекта ITimeProvider в качестве зависимости лучше, чем тратить время, потому что оно соответствует принципу «Не повторяйся».

Где-то в вашем рабочем коде что-то должно получить текущее время. Вы можете вывести код генерации даты за пределы вашего тестового покрытия или иметь один легко проверяемый объект, который может быть где угодно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...