ruby-on-rails datetime ruby-on-rails-4 rspec timecop

ruby on rails - Rails-Probando un método que usa DateTime.now



ruby-on-rails ruby-on-rails-4 (3)

Tengo un método que usa DateTime.now para realizar una búsqueda en algunos datos, quiero probar el método con varias fechas pero no sé cómo apagar DateTime.now ni puedo hacerlo funcionar con Timecop (si es que Funciona así).

Con el tiempo he intentado policia

it ''has the correct amount if falls in the previous month'' do t = "25 May".to_datetime Timecop.travel(t) puts DateTime.now expect(@employee.monthly_sales).to eq 150 end

cuando ejecuto la especificación puedo ver que pone DateTime.now da 2015-05-25T01:00:00+01:00 pero teniendo el mismo lugar pone DateTime.now dentro del método que estoy probando resultados 2015-07-24T08:57:53+01:00 (fecha de hoy). ¿Cómo puedo lograr esto?

------------------actualizar------------------------------- --------------------

Estaba configurando los registros (@employee, etc.) en un bloque before(:all) que parece haber causado el problema. Solo funciona cuando la configuración se realiza después del bloqueo de tiempo de Timecop do . ¿Por qué es este el caso?


Me encontré con varios problemas con Timecop y otras cosas mágicas que se meten con las clases de Fecha, Hora y Fecha y tiempo y sus métodos. Descubrí que es mejor usar simplemente la inyección de dependencia en su lugar:

Código de empleado

class Employee def monthly_sales(for_date = nil) for_date ||= DateTime.now # now calculate sales for ''for_date'', instead of current month end end

Especulación

it ''has the correct amount if falls in the previous month'' do t = "25 May".to_datetime expect(@employee.monthly_sales(t)).to eq 150 end

Nosotros, las personas del mundo Ruby, encontramos un gran placer al utilizar algunos trucos de magia, que las personas que usan lenguajes de programación menos expresivos no pueden utilizar. Pero este es el caso en el que la magia es demasiado oscura y realmente debería evitarse. Simplemente utilice el método de mejor práctica generalmente aceptado de inyección de dependencia en su lugar.


Timecop debería ser capaz de manejar lo que quieras. Intente congelar el tiempo antes de ejecutar la prueba en lugar de solo viajar, luego descongelar cuando termine. Me gusta esto:

before do t = "25 May".to_datetime Timecop.freeze(t) end after do Timecop.return end it ''has the correct amount if falls in the previous month'' do puts DateTime.now expect(@employee.monthly_sales).to eq 150 end

Del readme de Timecop:

congelar se utiliza para simular estáticamente el concepto de ahora. A medida que se ejecute su programa, Time.now no cambiará a menos que realice llamadas posteriores a la API de Timecop. por otro lado, viajar calcula un desplazamiento entre lo que actualmente creemos que Time.now (recuerda que admitimos viajes anidados) y el tiempo transcurrido. Utiliza este desplazamiento para simular el paso del tiempo.

Así que quieres congelar el tiempo en un lugar determinado, en lugar de simplemente viajar a ese tiempo. Ya que el tiempo pasará con un viaje como lo haría normalmente, pero desde un punto de partida diferente.

Si esto todavía no funciona, puedes poner tu método de llamada en un bloque con Timecop para asegurarte de que se está congelando el tiempo dentro del bloque como:

t = "25 May".to_datetime Timecop.travel(t) do # Or use freeze here, depending on what you need puts DateTime.now expect(@employee.monthly_sales).to eq 150 end


TL; DR : el problema era que se llamaba a DateTime.now en Employee antes que a Timecop.freeze en las especificaciones.

Timecop se burla del constructor de Time , Date y Date y Time . Cualquier instancia creada entre la freeze y el return (o dentro de un bloque de freeze ) se burlará.
Cualquier instancia creada antes de la freeze o después de la return no se verá afectada porque Timecop no se mete con los objetos existentes.

Desde el README (mi énfasis):

Una gema que ofrece capacidades de "viaje en el tiempo" y "tiempo de congelación", lo que hace que sea muy sencillo probar el código dependiente del tiempo. Proporciona un método unificado para simular Time.now, Date.today y DateTime.now en una sola llamada.

Por lo tanto, es esencial llamar a Timecop.freeze antes de crear el objeto Time que desea simular. Si se freeze en un bloque RSpec before , se ejecutará antes de que se evalúe el subject . Sin embargo, si tiene un bloque before donde configura su tema ( @employee en su caso), y tiene otro bloque before en una describe anidada, entonces su tema ya está configurado, ya que llamó a DateTime.new antes de que se congelara.

¿Qué sucede si agrega lo siguiente a su Employee

class Employee def now DateTime.now end end

A continuación, ejecute la siguiente especificación:

describe ''#now'' do let(:employee) { @employee } it ''has the correct amount if falls in the previous month'', focus: true do t = "25 May".to_datetime Timecop.freeze(t) do expect(DateTime.now).to eq t expect(employee.now).to eq t expect(employee.now.class).to be DateTime expect(employee.now.class.object_id).to be DateTime.object_id end end end

En lugar de usar un bloque de freeze , también puede freeze y return en rspec before y after ganchos:

describe Employee do let(:frozen_time) { "25 May".to_datetime } before { Timecop.freeze(frozen_time) } after { Timecop.return } subject { FactoryGirl.create :employee } it ''has the correct amount if falls in the previous month'' do # spec here end end

Fuera del tema, pero tal vez eche un vistazo a http://betterspecs.org/