ruby unit-testing time

ruby - Cómo falsificar Time.now?



unit-testing (14)

Acabo de tener esto en mi archivo de prueba:

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

y en mi archivo Time_helper.rb tengo un

def time_right_now current_time= Time.new return current_time end

así que cuando se prueba el time_right_now se sobrescribe para usar el tiempo que desee.

¿Cuál es la mejor manera de configurar Time.now con el fin de probar métodos sensibles al tiempo en una prueba unitaria?


Dependiendo de lo que esté comparando Time.now , a veces puede cambiar sus dispositivos para lograr el mismo objetivo o probar la misma función. Por ejemplo, tuve una situación en la que necesitaba que sucediera algo si alguna fecha estaba en el futuro y otra que sucedería si era en el pasado. Lo que pude hacer fue incluir en mis aparatos algunos rubíes incrustados (erb):

future: comparing_date: <%= Time.now + 10.years %> ... past: comparing_date: <%= Time.now - 10.years %> ...

Luego, en sus pruebas, usted elige cuál usar para probar las diferentes funciones o acciones en función del tiempo relativo a Time.now .


El Test::Redef reciente Test::Redef facilita esta y otras imposturas , incluso sin reestructurar el código en un estilo de inyección de dependencia (especialmente útil si está usando el código de otras personas).

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

Sin embargo, tenga cuidado con otras formas de obtener tiempo que esto no Date.new ( Date.new , una extensión compilada que hace su propia llamada al sistema, interfaces con cosas como servidores de bases de datos externas que conocen las marcas de tiempo actuales, etc.) Parece que La biblioteca Timecop anterior puede superar estas limitaciones.

Otros grandes usos incluyen probar cosas como "¿qué sucede cuando trato de usar este amigable cliente http, pero decide plantear esta excepción en lugar de devolverme una cadena?" sin configurar realmente las condiciones de la red que conducen a esa excepción (que puede ser complicado). También le permite verificar los argumentos para redefinir las funciones.


Este tipo de trabajo y permite la anidación:

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

Se podría mejorar ligeramente al indefinir los accesorios de pila y profundidad al final

Uso:

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


Estoy usando RSpec y lo hice: Time.stub! (: Now) .and_return (2.days.ago) antes de llamar a Time.now. De esa forma, puedo controlar el tiempo que utilicé para ese caso de prueba en particular


Hacer el time-warp

time-warp es una biblioteca que hace lo que quieres. Te da un método que toma un tiempo y un bloque y todo lo que sucede en el bloque usa el tiempo falso.

pretend_now_is(2000,"jan",1,0) do Time.now end


Me gusta mucho la biblioteca Timecop . Puedes hacer time warps en forma de bloque (al igual que time-warp):

Timecop.travel(6.days.ago) do @model = TimeSensitiveMode.new end assert @model.times_up!

(Sí, puedes anidar el viaje en el tiempo en forma de bloque).

También puede hacer un viaje declarativo en el tiempo:

class MyTest < Test::Unit::TestCase def setup Timecop.travel(...) end def teardown Timecop.return end end

Tengo algunos ayudantes de cucumber para Timecop here . Te dejan hacer cosas como estas:

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"


No olvide que el Time es simplemente una constante que se refiere a un objeto de clase. Si está dispuesto a provocar una advertencia, siempre puede hacer

real_time_class = Time Time = FakeTimeClass # run test Time = real_time_class


Personalmente prefiero hacer el reloj inyectable, así:

def hello(clock=Time) puts "the time is now: #{clock.now}" end

O:

class MyClass attr_writer :clock def initialize @clock = Time end def hello puts "the time is now: #{@clock.now}" end end

Sin embargo, muchos prefieren usar una biblioteca de burla / stubbing. En RSpec / flexmock puedes usar:

Time.stub!(:now).and_return(Time.mktime(1970,1,1))

O en Mocha:

Time.stubs(:now).returns(Time.mktime(1970,1,1))



Siempre Time.now en un método separado que convierto en attr_accessor en el simulacro.


También vea esta pregunta donde puse este comentario también.

Dependiendo de lo que esté comparando Time.now , a veces puede cambiar sus dispositivos para lograr el mismo objetivo o probar la misma función. Por ejemplo, tuve una situación en la que necesitaba que sucediera algo si alguna fecha estaba en el futuro y otra que sucedería si era en el pasado. Lo que pude hacer fue incluir en mis aparatos algunos rubíes incrustados (erb):

future: comparing_date: <%= Time.now + 10.years %> ... past: comparing_date: <%= Time.now - 10.years %> ...

Luego, en sus pruebas, usted elige cuál usar para probar las diferentes funciones o acciones en función del tiempo relativo a Time.now .


Tuve el mismo problema, tuve que simular tiempo para una especificación para un día específico y solo lo hice:

Time.stub!(:now).and_return(Time.mktime(2014,10,22,5,35,28))

esto te dará:

2014-10-22 05:35:28 -0700


Usando Rspec 3.2, la única manera simple si se encuentra para falsificar el valor de retorno de Time.now es:

now = Time.parse("1969-07-20 20:17:40") allow(Time).to receive(:now) { now }

Ahora Time.now siempre devolverá la fecha de aterrizaje del Apolo 11 en la luna.

Fuente: https://www.relishapp.com/rspec/rspec-mocks/docs