2012-05-18 14 views
17

Ich habe ein paywalled CMS + Fakturierungssystem für einen Kunden erstellt, und ich muss mit meinen Tests strenger werden.Simulieren des Zeitablaufs in Unitests

Ich behalte alle meine Daten in einem Django ORM und habe eine Reihe von Sellerie-Aufgaben, die in verschiedenen Intervallen laufen, um sicherzustellen, dass neue Rechnungen und Rechnungserinnerungen gesendet werden und Zugriffsbeschränkungen, wenn Benutzer ihre Rechnungen nicht bezahlen.

Zum Beispiel würde eine Ich mag die Lage sein, einen Test auszuführen, dass:

  1. einen neuen Benutzer erstellt und erzeugt eine Rechnung für X Tage Zugriff auf die Website

  2. die Simuliert Passing von X + 1 Tagen, und führt alle Aufgaben, die ich in Sellerie eingerichtet habe.

  3. Prüft, dass für den Benutzer eine neue Rechnung für weitere X Tage ausgestellt wurde.

Der KISS Ansatz, den ich mit so weit habe kommen ist es, alle Tests auf einer separaten Maschine zu tun, und tatsächlich das Datum/Zeit an der OS-Ebene zu manipulieren. Also das Testskript würde:

  1. das Systemdatum 1 bis Tag Set

  2. einen neuen Benutzer erstellen und die erste Rechnung für X Tage des Zugangs erzeugt

  3. Voraus dann Systemdatum 1 Tag . Führe alle meine Sellerieaufgaben aus. Wiederholen, bis X + 1 Tage Es wurden „bestanden“

  4. Überprüfen Sie, ob eine neue Rechnung

ausgestellt Es ist ein bisschen klobig, aber ich denke, dass es funktionieren könnte. Irgendwelche anderen Ideen, wie man es schafft?

Antwort

17

Sie können mock verwenden, um den Rückgabewert der Funktion zu ändern, die Sie verwenden, um die Uhrzeit zu erhalten (z. B. datetime.datetime.now).

Es gibt verschiedene Möglichkeiten, so (siehe Mock-Dokumentation) zu tun, aber hier ist eine:

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

Da wir nicht direkt datetime.datetime.now ersetzen können, erstellen wir eine gefälschte Datetime-Klasse, die alles tut das gleiche Weg, außer einen konstanten Wert zurückzugeben, wenn jetzt angerufen wird.

+0

Ich bekomme eine Fehlermeldung, wenn Sie dies versuchen: TypeError: kann Attribute der integrierten/Erweiterungstyp 'datetime.datetime' nicht festlegen.Trace: Traceback (letzter Aufruf zuletzt): // Datei "", Zeile 1, in // Datei "/usr/lib/python2.7/dist-packages/mock.py", Zeile 623, in __enter__ // setattr (self.target, self.attribute, new_attr) – Alfe

+0

Entschuldigung, das habe ich vergessen. Ich hatte dieses Problem kürzlich und habe einen Workaround. Gib mir ein paar Minuten und ich werde die Antwort aktualisieren. – madjar

+0

Hmm, habe die Version nicht gestartet, sorry. Nach dem Aufruf von 'setUp()' werden die Ergebnisse von 'datetime.datetime.now()' nicht durch das Setzen von 'self.time' beeinflusst. Vielleicht verstehe ich es immer noch falsch. – Alfe

4

Ohne die Verwendung einer speziellen Mock-Bibliothek, schlage ich vor, den Code für den Mock-up-Modus vorzubereiten (wahrscheinlich durch eine globale Variable). Im Mock-Up-Modus können Sie anstelle der normalen Zeitfunktion (wie time.time() oder was auch immer) eine Mock-Up-Zeitfunktion aufrufen, die in Ihrem speziellen Fall das zurückgibt, was Sie benötigen.

Ich würde für die Änderung der Systemzeit abstimmen. Das scheint kein Unit-Test zu sein, sondern eher wie ein Funktionstest, da es nicht parallel zu irgendetwas anderem auf dieser Maschine durchgeführt werden kann.