2012-05-18 11 views
17

He creado un sistema de facturación CMS + de pago para un cliente y necesito ser más estricto con mis pruebas.Simulando el paso del tiempo en la prueba unitaria

Guardo todos mis datos en un Django ORM y tengo un montón de tareas de Apio que se ejecutan en diferentes intervalos que aseguran que se envíen nuevas facturas y recordatorios de facturas y cortes de acceso cuando los usuarios no pagan sus facturas.

Por ejemplo, me gustaría ser capaz de ejecutar una prueba que:

  1. crea un nuevo usuario y genera una factura por X días de acceso al sitio

  2. simula el pasando de X + 1 día, y ejecuta todas las tareas que tengo configuradas en Apio.

  3. Comprueba que se haya emitido una nueva factura para otros X días para el usuario.

El enfoque de KISS que he encontrado hasta el momento es hacer todas las pruebas en una máquina separada y en realidad manipular la fecha/hora en el nivel de sistema operativo. Así que la secuencia de comandos de prueba sería:

  1. Establecer la fecha del sistema a día 1

  2. Crear un nuevo usuario y generar la primera factura por X días de acceso

  3. Avance entonces la fecha del sistema 1 día . Ejecuta todas mis tareas de apio. Repita hasta X + 1 días han "pasado"

  4. Compruebe que una nueva factura se ha emitido

Es un poco torpe, pero creo que podría funcionar. ¿Alguna otra idea sobre cómo hacerlo?

Respuesta

17

Puede usar mock para cambiar el valor de retorno de la función que usa para obtener el tiempo (datetime.datetime.now por ejemplo).

Hay varias maneras de hacerlo (véase la documentación simulado), pero aquí es una:

import unittest 
import datetime 
from mock import patch 

class SomeTestCase(unittest.TestCase): 
    def setUp(self): 
     self.time = datetime.datetime(2012, 5, 18) 
     class fakedatetime(datetime.datetime): 
      @classmethod 
      def now(cls): 
       return self.time 
     patcher = patch('datetime.datetime', fakedatetime) 
     self.addCleanup(patcher.stop) 
     patcher.start() 

    def test_something(self): 
     self.assertEqual(datetime.datetime.now(), datetime.datetime(2012, 5, 18)) 
     self.time = datetime.datetime(2012, 5, 20) 
     self.assertEqual(datetime.datetime.now(), datetime.datetime(2012, 5, 20)) 

Porque no podemos reemplazar directamente datetime.datetime.now, creamos una clase de fecha y hora falso que hace todo el mismo manera, excepto que devuelve un valor constante cuando se llama ahora.

+0

Aparece un error al intentar esto: TypeError: no se pueden establecer los atributos del tipo de extensión/incorporada 'datetime.datetime'.Rastreo: Rastreo (llamada más reciente): // Archivo "", línea 1, en // Archivo "/usr/lib/python2.7/dist-packages/mock.py", línea 623, en __enter__ // setattr (self.target, self.attribute, new_attr) – Alfe

+0

Disculpe, lo olvidé. He tenido ese problema recientemente, y tengo una solución alternativa. Solo dame unos minutos y actualizaré la respuesta. – madjar

+0

Hmm, no obtuve la versión ejecutándose también, lo siento. Después de llamar a 'setUp()' los resultados de 'datetime.datetime.now()' no se ven afectados por la configuración 'self.time'. Tal vez me estoy equivocando todavía. – Alfe

4

Sin el uso de una biblioteca de simulación especial, propongo preparar el código para estar en modo simulación (probablemente por una variable global). En el modo de maqueta en lugar de llamar a la función de tiempo normal (como time.time() o lo que sea), puede llamar a una función de tiempo de maqueta que devuelve lo que necesita en su caso especial.

Yo votaría en contra de cambiar la hora del sistema. Eso no parece una prueba unitaria, sino más bien como una prueba funcional, ya que no se puede hacer en paralelo a cualquier otra cosa en esa máquina.

Cuestiones relacionadas