2012-05-18 12 views
17

Ho costruito un sistema di fatturazione CMS + paywalled per un client e ho bisogno di essere più severo con i miei test.Simulazione del passare del tempo in unittest

Conservo tutti i miei dati in un ORG Django e ho un sacco di attività di Celery che funzionano a intervalli diversi che assicurano che nuove fatture e promemoria di fatture vengano inviate e tagli di accesso quando gli utenti non pagano le loro fatture.

Per esempio mi piacerebbe essere un grado di eseguire un test che:

  1. crea un nuovo utente e genera una fattura per X giorni di accesso al sito

  2. simula il passaggio di X + 1 giorni e esegue tutti i compiti che ho impostato su Celery.

  3. Verifica che una nuova fattura per altri X giorni sia stata rilasciata all'utente.

L'approccio BACIO mi è venuta in mente finora è quello di fare tutti i test su una macchina separata ed effettivamente manipolare la data/ora a livello di sistema operativo. Quindi lo script di test sarebbe:

  1. Impostare la data di sistema al giorno 1

  2. Creare un nuovo utente e generare la prima fattura per X giorni di accesso

  3. Advance poi data di sistema 1 giorno . Gestisci tutti i miei compiti di sedano. Ripetere fino a X + 1 giorni sono "passati"

  4. Controllare che una nuova fattura è stata emessa

E 'un po' goffo, ma penso che potrebbe funzionare. Qualche altra idea su come ottenerlo?

risposta

17

È possibile utilizzare mock per modificare il valore restituito della funzione utilizzata per ottenere il tempo (ad esempio datetime.datetime.now).

Ci sono vari modi per farlo (consultare la documentazione finto), ma qui è uno:

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)) 

Perché non possiamo sostituire direttamente datetime.datetime.now, creiamo una classe datetime falso che fa tutto lo stesso modo, tranne che restituisce un valore costante quando viene chiamato.

+0

Viene visualizzato un errore durante il tentativo: TypeError: impossibile impostare gli attributi di tipo built-in/extension "datetime.datetime".Traccia: Traceback (ultima chiamata ultima): // File "", riga 1, in // File "/usr/lib/python2.7/dist-packages/mock.py", riga 623, in __enter__ // setattr (self.target, self.attribute, new_attr) – Alfe

+0

Scusami, l'ho dimenticato. Ho avuto questo problema di recente, e ho una soluzione alternativa. Dammi solo qualche minuto e aggiornerò la risposta. – madjar

+0

Hmm, non è stata eseguita anche la versione, mi dispiace. Dopo aver chiamato 'setUp()' i risultati di 'datetime.datetime.now()' non sono influenzati dall'impostazione di 'self.time'. Forse mi sto sbagliando ancora. – Alfe

4

Senza l'uso di una libreria di simulazione speciale, propongo di preparare il codice per essere in modalità mock-up (probabilmente con una variabile globale). In modalità mock-up invece di chiamare la normale funzione orario (come time.time() o qualsiasi altra cosa) si può chiamare una funzione di simulazione del tempo che restituisce tutto ciò che è necessario nel caso particolare.

Vorrei votare per modificare l'ora del sistema. Questo non sembra un test unitario ma piuttosto come un test funzionale in quanto non può essere eseguito in parallelo a qualsiasi altra cosa su quella macchina.