Kann mir jemand sagen, warum das nicht funktioniert?
>>> import mock
>>> @mock.patch('datetime.date.today')
... def today(cls):
... return date(2010, 1, 1)
...
>>> from datetime import date
>>> date.today()
datetime.date(2010, 12, 19)
Vielleicht könnte jemand einen besseren Weg vorschlagen?
mock
Bibliothek: voidspace.org.uk/python/mock/examples.html#partial-mockingAntworten:
Es gibt einige Probleme.
Erstens ist die Art und Weise, wie Sie sie verwenden,
mock.patch
nicht ganz richtig. Bei Verwendung als Dekorateur wird die angegebene Funktion / Klasse (in diesem Falldatetime.date.today
) nur innerhalb der dekorierten Funktion durch einMock
Objekt ersetzt . Also nur in deinem Willentoday()
datetime.date.today
wird eine andere Funktion sein, die nicht das zu sein scheint, was Sie wollen.Was Sie wirklich wollen, scheint eher so zu sein:
Leider funktioniert das nicht:
Dies schlägt fehl, da die in Python integrierten Typen unveränderlich sind - siehe diese Antwort Weitere Informationen .
In diesem Fall würde ich datetime.date selbst unterordnen und die richtige Funktion erstellen:
Und jetzt könnten Sie tun:
quelle
datetime
ursprünglichen Wert der Instanz wiederherstellen ? mitdeepcoppy
?patch('mymodule.datetime', Mock(today=lambda: date(2017, 11, 29)))
@patch('module_you_want_to_test.date', Mock( today=Mock(return_value=datetime.date(2017, 11, 29))))
.Eine weitere Option ist die Verwendung von https://github.com/spulec/freezegun/.
Es installieren:
Und benutze es:
Dies wirkt sich auch auf andere datetime-Aufrufe in Methodenaufrufen von anderen Modulen aus:
other_module.py:
main.py:
Und schlussendlich:
quelle
Für das, was es wert ist, sprechen die Mock-Dokumente speziell über datetime.date.today, und dies ist möglich, ohne eine Dummy-Klasse erstellen zu müssen:
https://docs.python.org/3/library/unittest.mock-examples.html#partial-mocking
quelle
from datetime import date
es der Name des Moduls ist, in demfrom datetime import date
und der Aufruf zudate.today()
erscheintIch glaube, ich bin etwas spät gekommen, aber ich denke, das Hauptproblem hier ist, dass Sie datetime.date.today direkt patchen und dies laut Dokumentation falsch ist.
Sie sollten beispielsweise die Referenz patchen, die in die Datei importiert wurde, in der sich die getestete Funktion befindet.
Angenommen, Sie haben eine Datei functions.py, in der Sie Folgendes haben:
dann sollten Sie in Ihrem Test so etwas haben
Hoffe das hilft ein bisschen.
quelle
NameError: name 'datetime' is not defined
). Woher kommt diedatetime.strptime
Referenz inMock(return_value=...)
, wenn Sie nichtdatetime
in Ihre Testdatei importieren ? UPDATE: Es ist in Ordnung, ich habe gerade dasdatetime
Modul in die Testdatei importiert . Ich dachte, der Trick war, wie Sie diedatetime
Referenz aus der Testdatei verstecken .import datetime
oderfrom datetime import strptime
? Wenn Sie die erstedatetime
machen würdenmocked_datetime.strptime.return_value = whatever
, müssten Sie verspotten und tun , die spätere, Sie müssten die strptime-Referenz in der Datei, in der sich die getestete Methode befindet, direkt verspotten.Mock(return_value=datetime...)
funktioniert.So fügen Sie Daniel Gs Lösung hinzu :
Dadurch wird eine Klasse erstellt, die bei Instanziierung ein normales datetime.date-Objekt zurückgibt, aber auch geändert werden kann.
quelle
date
/datetime
selbst, sondern verwendet die global verfügbare Variable, sodass es kein Problem geben sollte: dpaste.com/790310Ich war vor ein paar Tagen mit der gleichen Situation konfrontiert, und meine Lösung bestand darin, eine Funktion im Modul zu definieren, um dies zu testen und einfach zu verspotten:
Heute habe ich von FreezeGun erfahren und es scheint diesen Fall wunderschön abzudecken
quelle
Der einfachste Weg für mich ist dies:
VORSICHT für diese Lösung: Alle Funktionen von
datetime module
vontarget_module
funktionieren nicht mehr.quelle
datetime_mock.now = Mock(return_value=datetime(1999, 1, 1)
könnte sogar auf gekürzt werdendatetime_mock.now.return_value = datetime(1999, 1, 1)
. Anstatt den Patch mit zu startenstart()
, sollten Sie denwith patch(...):
Kontextmanager verwenden, um sicherzustellen, dass erdatetime
sich nach Beendigung Ihres Tests wieder normal (nicht verspottet) verhält.datetime.datetime.now()
verspottet ^^?datetime module
abtarget_module
funktionieren.Sie können den folgenden Ansatz verwenden, der auf der Daniel G-Lösung basiert. Dieser hat den Vorteil, dass die Typprüfung nicht unterbrochen wird
isinstance(d, datetime.date)
.Grundsätzlich ersetzen wir die C-basierte
datetime.date
Klasse durch unsere eigene Python-Unterklasse, die Originalinstanzen erzeugtdatetime.date
und aufisinstance()
Anfragen genau wie native antwortetdatetime.date
.Verwenden Sie es als Kontextmanager in Ihren Tests:
Ein ähnlicher Ansatz kann verwendet werden, um die
datetime.datetime.now()
Funktion zu verspotten .quelle
__instancecheck__
Methode eine maximale Rekursionstiefe RuntimeError .Im Allgemeinen hätten Sie
datetime
oder vielleichtdatetime.date
in ein Modul irgendwo importiert. Eine effektivere Möglichkeit, die Methode zu verspotten, besteht darin, sie auf dem Modul zu patchen, das sie importiert. Beispiel:a.py.
Dann wird für Ihren Test das Scheinobjekt selbst als Argument an die Testmethode übergeben. Sie würden das Modell mit dem gewünschten Ergebniswert einrichten und dann Ihre zu testende Methode aufrufen. Dann würden Sie behaupten, dass Ihre Methode getan hat, was Sie wollen.
Ein Wort der Warnung. Es ist mit Sicherheit möglich, mit Spott über Bord zu gehen. Wenn Sie dies tun, werden Ihre Tests länger, schwerer zu verstehen und unmöglich zu warten. Bevor Sie eine Methode so einfach verspotten wie
datetime.date.today
, fragen Sie sich , wenn Sie wirklich brauchen , um es zu verspotten. Wenn Ihr Test kurz und präzise ist und ohne Verspottung der Funktion einwandfrei funktioniert, sehen Sie sich möglicherweise nur ein internes Detail des zu testenden Codes an und nicht ein Objekt, das Sie verspotten müssen.quelle
Hier ist eine andere Möglichkeit,
datetime.date.today()
mit einem zusätzlichen Bonus zu verspotten, dass die restlichendatetime
Funktionen weiterhin funktionieren, da das Scheinobjekt so konfiguriert ist, dass es das ursprünglichedatetime
Modul umschließt:Beachten Sie das
wraps=datetime
Argument:mock.patch()
- Wennfoo_module
anderedatetime
Funktionen verwendet werdendate.today()
, werden diese an das ursprünglich umschlossenedatetime
Modul weitergeleitet.quelle
Verschiedene Lösungen werden unter http://blog.xelnor.net/python-mocking-datetime/ erläutert. . Zusammenfassend:
Mock - Objekt - Einfach und effizient , aber Pausen isinstance () prüft:
Scheinklasse
Benutzen als:
quelle
Vielleicht könnten Sie Ihre eigene "today ()" - Methode verwenden, die Sie bei Bedarf patchen. Ein Beispiel mit verspottetem utcnow () finden Sie hier: https://bitbucket.org/k_bx/blog/src/tip/source/en_posts/2012-07-13-double-call-hack.rst?at=default
quelle
Ich habe die @ user3016183-Methode mit einem benutzerdefinierten Dekorator implementiert:
Ich dachte, das könnte eines Tages jemandem helfen ...
quelle
Es ist möglich, Funktionen aus dem
datetime
Modul zu verspotten, ohne sie hinzuzufügenside_effects
quelle
Für diejenigen unter Ihnen, die Pytest mit Spötter verwenden, ist hier, wie ich verspottet habe,
datetime.datetime.now()
was der ursprünglichen Frage sehr ähnlich ist.Im Wesentlichen muss der Mock eingestellt werden, um das angegebene Datum zurückzugeben. Sie können das Objekt von datetime nicht direkt patchen.
quelle
Ich habe diese Arbeit durch den Import
datetime
alsrealdatetime
und Ersetzen der Methoden , die ich mit den realen Methoden in der Mock benötigt:quelle
Sie können dies verspotten
datetime
:Im Modul
sources.py
:In Ihrem
tests.py
:quelle
sources
in Ihrem Patch-Dekorateur?CPython implementiert das datetime-Modul tatsächlich mit einer reinen Python- Bibliothek / datetime.py und einem C-optimierten Modul / _datetimemodule.c . Die C-optimierte Version kann nicht gepatcht werden, die reine Python-Version jedoch.
Am Ende der Pure-Python-Implementierung in Lib / datetime.py befindet sich folgender Code:
Dieser Code importiert alle C-optimierten Definitionen und ersetzt effektiv alle reinen Python-Definitionen. Wir können CPython zwingen, die reine Python-Implementierung des datetime-Moduls zu verwenden, indem wir Folgendes tun:
Durch die Einstellung
sys.modules["_datetime"] = None
weisen wir Python an, das C-optimierte Modul zu ignorieren. Dann laden wir das Modul neu, das den Import von verursacht_datetime
fehlschlägt. Jetzt bleiben die reinen Python-Definitionen erhalten und können normal gepatcht werden.Wenn Sie Pytest verwenden Snippet in conftest.py ein, und Sie können
datetime
Objekte normal patchen .quelle