tutorial example español descargar composer php phpunit

example - phpunit tutorial español



¿Puedo "simular" el tiempo en PHPUnit? (10)

... no saber si ''simulacro'' es la palabra correcta.

De todos modos, tengo una base de código heredada por la que estoy tratando de escribir algunas pruebas porque están basadas en el tiempo. Al tratar de no ser demasiado vago, el código se relaciona con mirar el historial de un elemento y determinar si ese elemento ahora se basa en un umbral de tiempo.

En algún momento también necesito probar agregar algo a ese historial y verificar que el umbral se haya cambiado (y, obviamente, correcto).

El problema que estoy detectando es que parte del código que estoy probando está usando call to time (), por lo que me resulta muy difícil saber exactamente cuál debe ser el tiempo límite, basado en el hecho de que No estoy muy seguro de cuándo se llamará esa función time ().

Así que mi pregunta es básicamente esta: ¿hay alguna manera para que yo pueda "anular" la llamada time (), o de alguna manera "burlarme" del tiempo, de modo que mis pruebas estén funcionando en un "tiempo conocido"?

¿O simplemente tengo que aceptar el hecho de que voy a tener que hacer algo en el código que estoy probando, para que de alguna manera me permita forzarlo a usar un tiempo determinado si es necesario?

De cualquier manera, ¿hay alguna ''práctica común'' para desarrollar una funcionalidad sensible al tiempo que sea fácil de probar?

Edición: Parte de mi problema también es el hecho de que el momento en que ocurrieron las cosas en la historia afecta el umbral. Aquí hay un ejemplo de parte de mi problema ...

Imagina que tienes un plátano y tratas de hacer ejercicio cuando es necesario comerlo. Digamos que caducará dentro de los 3 días, a menos que se rocíe con algún producto químico, en cuyo caso agregamos 4 días a la expiración, desde el momento en que se aplicó el rociado . Luego, podemos agregarle otros 3 meses congelando, pero si se ha congelado, solo tenemos 1 día para usarlo después de que se haya descongelado.

Todas estas reglas son dictadas por tiempos históricos. Estoy de acuerdo en que podría usar la sugerencia de pruebas de Dominik en unos segundos, pero ¿qué hay de mis datos históricos? ¿Debería simplemente ''crear'' eso sobre la marcha?

Como puedes o no puedes decir, todavía estoy tratando de entender todo este concepto de "prueba";)


Descargo de responsabilidad: escribí esta biblioteca.

Puedes simular el tiempo para la prueba usando Clock from ouzo-goodies .

En el código de uso simplemente:

$time = Clock::now();

Luego en las pruebas:

Clock::freeze(''2014-01-07 12:34''); $result = Class::getCurrDate(); $this->assertEquals(''2014-01-07'', $result);


Aquí hay una adición a la publicación de fab. Hice la anulación basada en el espacio de nombres usando una eval. De esta manera, puedo ejecutarlo para pruebas y no para el resto de mi código. Ejecuto una función similar a:

function timeOverrides($namespaces = array()) { $returnTime = time(); foreach ($namespaces as $namespace) { eval("namespace $namespace; function time() { return $returnTime; }"); } }

luego pase en timeOverrides(array(...)) en la configuración de la prueba para que mis pruebas solo tengan que hacer un seguimiento de los espacios de nombres en los que se llama el tiempo ().


En la mayoría de los casos esto servirá. Tiene algunas ventajas:

  • no tienes que burlarte de nada
  • no necesitas complementos externos
  • puede utilizar cualquier función de tiempo, no solo time () sino también objetos DateTime
  • no es necesario utilizar espacios de nombres.

Está utilizando phpunit, pero puede adaptarlo a cualquier otro marco de prueba, solo necesita una función que funcione como assertContains () de phpunit.

1) Agregue la siguiente función a su clase de prueba o bootstrap. La tolerancia predeterminada para el tiempo es de 2 segundos. Puede cambiarlo pasando 3er argumento a assertTimeEquals o modificar la función args.

private function assertTimeEquals($testedTime, $shouldBeTime, $timeTolerance = 2) { $toleranceRange = range($shouldBeTime, $shouldBeTime+$timeTolerance); return $this->assertContains($testedTime, $toleranceRange); }

2) Ejemplo de prueba:

public function testGetLastLogDateInSecondsAgo() { // given $date = new DateTime(); $date->modify(''-189 seconds''); // when $this->setLastLogDate($date); // then $this->assertTimeEquals(189, $this->userData->getLastLogDateInSecondsAgo()); }

assertTimeEquals () comprobará si la matriz de (189, 190, 191) contiene 189.

Esta prueba se debe pasar para que la función de trabajo sea correcta. SI la ejecución de la función de prueba toma menos de 2 segundos.

No es perfecto ni muy exacto, pero es muy simple y en muchos casos es suficiente para probar lo que se quiere probar.


La solución más simple sería anular la función PHP time () y reemplazarla con su propia versión. Sin embargo, no puede reemplazar las funciones PHP incorporadas fácilmente ( vea aquí ).

Aparte de eso, la única forma es abstraer la llamada time a una clase / función propia que devuelva el tiempo que necesita para las pruebas.

Alternativamente, puede ejecutar el sistema de prueba (sistema operativo) en una máquina virtual y cambiar la hora de toda la computadora virtual.



Personalmente, sigo usando time () en las funciones / métodos probados. En su código de prueba, asegúrese de no probar la igualdad con el tiempo (), sino simplemente una diferencia de tiempo de menos de 1 o 2 (dependiendo de cuánto tiempo demore la función en ejecutarse)


Puede anular la función time () de php usando la extensión runkit. Asegúrate de configurar runkit.internal_overide en On


Recientemente se me ocurrió otra solución que es excelente si está usando espacios de nombres PHP 5.3. Puede implementar una nueva función time () dentro de su espacio de nombres actual y crear un recurso compartido donde establezca el valor de retorno en sus pruebas. Entonces, cualquier llamada a time () no calificada usará su nueva función.

Para más información lo describí en detalle en mi blog


Tuve que simular una solicitud particular en fecha futura y pasada en la propia aplicación (no en Pruebas de unidad). Por lo tanto, todas las llamadas a / DateTime :: now () deben devolver la fecha que se estableció previamente en toda la aplicación.

Decidí ir con esta biblioteca https://github.com/rezzza/TimeTraveler , ya que puedo burlarme de las fechas sin alterar todos los códigos.

/Rezzza/TimeTraveler::enable(); /Rezzza/TimeTraveler::moveTo(''2011-06-10 11:00:00''); var_dump(new /DateTime()); // 2011-06-10 11:00:00 var_dump(new /DateTime(''+2 hours'')); // 2011-06-10 13:00:00


Usando la extensión [runkit] [1]:

define(''MOCK_DATE'', ''2014-01-08''); define(''MOCK_TIME'', ''17:30:00''); define(''MOCK_DATETIME'', MOCK_DATE.'' ''.MOCK_TIME); private function mockDate() { runkit_function_rename(''date'', ''date_real''); runkit_function_add(''date'',''$format="Y-m-d H:i:s", $timestamp=NULL'', ''$ts = $timestamp ? $timestamp : strtotime(MOCK_DATETIME); return date_real($format, $ts);''); } private function unmockDate() { runkit_function_remove(''date''); runkit_function_rename(''date_real'', ''date''); }

Incluso puedes probar el simulacro de esta manera:

public function testMockDate() { $this->mockDate(); $this->assertEquals(MOCK_DATE, date(''Y-m-d'')); $this->assertEquals(MOCK_TIME, date(''H:i:s'')); $this->assertEquals(MOCK_DATETIME, date()); $this->unmockDate(); }