Czy ktoś może mi powiedzieć, dlaczego to nie działa?
>>> 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)
Może ktoś mógłby zasugerować lepszy sposób?
mock
biblioteki: voidspace.org.uk/python/mock/examples.html#partial-mockingOdpowiedzi:
Jest kilka problemów.
Po pierwsze, sposób, w jaki używasz,
mock.patch
nie jest do końca właściwy. Gdy jest używany jako dekorator, zastępuje daną funkcję / klasę (w tym przypadkudatetime.date.today
)Mock
przedmiotem tylko w funkcji dekorowanej . Tak więc tylko w twojejtoday()
wolidatetime.date.today
będzie inna funkcja, która nie wydaje się być tym, czego chcesz.To, czego naprawdę chcesz, wydaje się bardziej podobne do tego:
Niestety to nie zadziała:
Nie udaje się to, ponieważ typy wbudowane Pythona są niezmienne - zobacz tę odpowiedź, aby uzyskać więcej informacji.
W tym przypadku podklasowałbym datetime.date siebie i utworzył odpowiednią funkcję:
A teraz możesz zrobić:
źródło
datetime
instancji do jej pierwotnej wartości? zdeepcoppy
?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))))
.Inną opcją jest użycie https://github.com/spulec/freezegun/
Zainstaluj to:
I użyj go:
Wpływa również na inne wywołania datetime w wywołaniach metod z innych modułów:
other_module.py:
main.py:
I w końcu:
źródło
Co jest warte, dokumentacja Mock mówi konkretnie o datetime.date.today i można to zrobić bez konieczności tworzenia fikcyjnej klasy:
https://docs.python.org/3/library/unittest.mock-examples.html#partial-mocking
źródło
from datetime import date
to jest to nazwa modułu, w którym pojawiafrom datetime import date
się wezwaniedate.today()
Wydaje mi się, że trochę się spóźniłem na to, ale myślę, że głównym problemem jest to, że bezpośrednio łatasz datetime.date.today i zgodnie z dokumentacją jest to błędne.
Na przykład należy załatać odniesienie zaimportowane w pliku, w którym znajduje się testowana funkcja.
Załóżmy, że masz plik functions.py, w którym są następujące:
wtedy w swoim teście powinieneś mieć coś takiego
Mam nadzieję, że to trochę pomoże.
źródło
NameError: name 'datetime' is not defined
). Skąd pochodzidatetime.strptime
odwołanie,Mock(return_value=...)
jeśli nie importujeszdatetime
pliku testowego? AKTUALIZACJA: W porządku, po prostu zaimportowałemdatetime
moduł do pliku testowego. Pomyślałem, że sztuczkadatetime
polega na tym, że ukrywasz odniesienie z pliku testowego.import datetime
czyfrom datetime import strptime
? gdybyś robił pierwszą z nich, musiałbyś kpićdatetime
i robiłamocked_datetime.strptime.return_value = whatever
, to druga, musiałbyś bezpośrednio mockować referencję strptime w pliku, w którym znajduje się testowana metoda.Mock(return_value=datetime...)
działało.Aby dodać do rozwiązania Daniela G :
Tworzy to klasę, która po utworzeniu zwróci normalny obiekt datetime.date, ale którą można również zmienić.
źródło
date
/datetime
sama, używa zmiennej globalnie dostępnej, więc nie powinno być problemu: dpaste.com/790310Kilka dni temu miałem taką samą sytuację, a moim rozwiązaniem było zdefiniowanie funkcji w module do testowania i po prostu mock:
Dziś dowiedziałem się o FreezeGun i wydaje się, że pięknie pokrywa tę sprawę
źródło
Najłatwiej dla mnie zrobić to:
UWAGA dla tego rozwiązania: wszystkie funkcje od
datetime module
odtarget_module
pracy zostanie zatrzymane.źródło
datetime_mock.now = Mock(return_value=datetime(1999, 1, 1)
można było nawet skrócić dodatetime_mock.now.return_value = datetime(1999, 1, 1)
. Zamiast rozpoczynać łatkęstart()
za pomocą, rozważ użyciewith patch(...):
menedżera kontekstu, aby upewnić się, żedatetime
po zakończeniu testu zachowuje się normalnie (odblokowany).datetime.datetime.now()
odblokować ^^?datetime module
odtarget_module
przestanie działać.Możesz zastosować następujące podejście, oparte na rozwiązaniu Daniela G. Ten ma tę zaletę, że nie przerywa sprawdzania typów
isinstance(d, datetime.date)
.Zasadniczo zastępujemy
datetime.date
klasę opartą na języku C naszą własną podklasą Pythona, która tworzy oryginalnedatetime.date
instancje i odpowiada naisinstance()
zapytania dokładnie tak samo jak natywnadatetime.date
.Użyj go jako menedżera kontekstu w swoich testach:
Podobne podejście można zastosować do kpiny z
datetime.datetime.now()
funkcji.źródło
__instancecheck__
metodą.Ogólnie rzecz biorąc, musiałbyś
datetime
lub możedatetime.date
gdzieś zaimportować do modułu. Bardziej efektywnym sposobem na oszukanie metody byłoby załatanie jej na module, który ją importuje. Przykład:a.py
Następnie na potrzeby testu sam obiekt pozorowany zostanie przekazany jako argument do metody testowej. Możesz skonfigurować makietę z żądaną wartością wyniku, a następnie wywołać testowaną metodę. Następnie zapewniłbyś, że twoja metoda zrobiła to, co chcesz.
Słowo ostrzeżenia. Z kpiną z całą pewnością można przejść za burtę. Kiedy to zrobisz, twoje testy będą dłuższe, trudniejsze do zrozumienia i niemożliwe do utrzymania. Zanim zaczniesz kpić z tak prostej metody
datetime.date.today
, zadaj sobie pytanie, czy naprawdę musisz z niej kpić. Jeśli twój test jest krótki i rzeczowy i działa dobrze bez kpiny z funkcji, możesz po prostu patrzeć na wewnętrzny szczegół testowanego kodu, a nie obiekt, który chcesz wyszydzić.źródło
Oto inny sposób na mockowanie
datetime.date.today()
z dodatkową premią polegającą na tym, że pozostałedatetime
funkcje nadal działają, ponieważ obiekt pozorowany jest skonfigurowany do pakowania oryginalnegodatetime
modułu:Zwróć uwagę na
wraps=datetime
argumentmock.patch()
- gdyfoo_module
używa innychdatetime
funkcji opróczdate.today()
tego, zostaną one przekazane do oryginalnego opakowanegodatetime
modułu.źródło
Kilka rozwiązań omówiono w http://blog.xelnor.net/python-mocking-datetime/ . W podsumowaniu:
Mock object - Prosty i skuteczny, ale przerywa sprawdzanie instancji ():
Mock klasa
Użyj jako:
źródło
Może przydałaby się Twoja własna metoda „Today ()”, którą można załatać w razie potrzeby. Przykład z mocking utcnow () można znaleźć tutaj: https://bitbucket.org/k_bx/blog/src/tip/source/en_posts/2012-07-13-double-call-hack.rst?at=default
źródło
Zaimplementowałem metodę @ user3016183 używając niestandardowego dekoratora:
Pomyślałem, że to może kiedyś komuś pomóc ...
źródło
Możliwe jest mockowanie funkcji z
datetime
modułu bez dodawaniaside_effects
źródło
Dla tych z Was korzystających z pytesta z mockerem tutaj jest jak kpiłem
datetime.datetime.now()
co jest bardzo podobne do oryginalnego pytania.Zasadniczo makietę należy ustawić tak, aby zwracała określoną datę. Nie możesz bezpośrednio załatać obiektu datetime.
źródło
Wykonałem tę pracę, importując
datetime
jakorealdatetime
i zastępując metody, których potrzebowałem w makiecie, metodami rzeczywistymi:źródło
Możesz kpić
datetime
za pomocą tego:W module
sources.py
:W twoim
tests.py
:źródło
sources
w twoim dekoratorze łat?CPython faktycznie implementuje moduł datetime, używając zarówno czystego języka Python Lib / datetime.py, jak i zoptymalizowanych pod kątem C modułów / _datetimemodule.c . Wersja zoptymalizowana pod kątem języka C nie może być łatana, ale wersja oparta na czystym Pythonie tak.
U dołu implementacji czystego Pythona w Lib / datetime.py znajduje się następujący kod:
Ten kod importuje wszystkie definicje zoptymalizowane pod kątem języka C i skutecznie zastępuje wszystkie definicje w czystym języku Python. Możemy zmusić CPythona do używania implementacji modułu datetime w czystym Pythonie, wykonując:
Ustawiając
sys.modules["_datetime"] = None
, mówimy Pythonowi, aby ignorował moduł zoptymalizowany pod kątem C. Następnie przeładowujemy moduł, który powoduje import z_datetime
niepowodzenie . Teraz definicje czystego Pythona pozostają i można je normalnie załatać.Jeśli używasz Pytest, dołącz powyższy fragment kodu do conftest.py i możesz
datetime
normalnie łatać obiekty.źródło