Adakah yang bisa memberi tahu saya mengapa ini tidak berhasil?
>>> 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)
Mungkin seseorang bisa menyarankan cara yang lebih baik?
mock
perpustakaan: voidspace.org.uk/python/mock/examples.html#partial-mockingJawaban:
Ada beberapa masalah.
Pertama-tama, cara Anda menggunakan
mock.patch
tidak sepenuhnya benar. Ketika digunakan sebagai dekorator, ia menggantikan fungsi / kelas yang diberikan (dalam hal ini,datetime.date.today
) denganMock
objek hanya dalam fungsi yang didekorasi . Jadi, hanya di dalam Andatoday()
akandatetime.date.today
fungsi yang berbeda, yang tampaknya tidak seperti yang Anda inginkan.Apa yang sebenarnya Anda inginkan tampaknya lebih seperti ini:
Sayangnya, ini tidak akan berhasil:
Ini gagal karena tipe bawaan Python tidak dapat diubah - lihat jawaban ini untuk lebih jelasnya.
Dalam hal ini, saya akan membuat subclass datetime.date sendiri dan membuat fungsi yang tepat:
Dan sekarang Anda bisa melakukannya:
sumber
datetime
instance ke nilai aslinya? dengandeepcoppy
?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))))
.Opsi lain adalah menggunakan https://github.com/spulec/freezegun/
Pasang itu:
Dan gunakan itu:
Ini juga memengaruhi panggilan datetime lainnya dalam panggilan metode dari modul lain:
other_module.py:
main.py:
Dan akhirnya:
sumber
Untuk apa nilainya, dokumen Mock berbicara tentang datetime.date.today khusus, dan mungkin untuk melakukan ini tanpa harus membuat kelas dummy:
https://docs.python.org/3/library/unittest.mock-examples.html#partial-mocking
sumber
from datetime import date
maka itu adalah nama modul di manafrom datetime import date
dan panggilan untukdate.today()
munculSaya kira saya datang sedikit terlambat untuk ini tetapi saya pikir masalah utama di sini adalah bahwa Anda menambal datetime.date.today secara langsung dan, menurut dokumentasi, ini salah.
Anda harus menambal referensi yang diimpor dalam file tempat fungsi yang diuji, misalnya.
Katakanlah Anda memiliki file functions.py di mana Anda memiliki yang berikut ini:
maka, dalam ujian Anda, Anda harus memiliki sesuatu seperti ini
Semoga ini bisa membantu sedikit.
sumber
NameError: name 'datetime' is not defined
). Dari manadatetime.strptime
referensiMock(return_value=...)
berasal jika Anda tidak mengimpordatetime
dalam file pengujian Anda? PEMBARUAN: Tidak apa-apa, saya hanya melanjutkan dan mengimpordatetime
modul dalam file uji. Saya pikir triknya adalah bagaimana Anda menyembunyikandatetime
referensi dari file tes.import datetime
ataufrom datetime import strptime
? jika Anda melakukan yang pertama, Anda harus mengejekdatetime
dan melakukannyamocked_datetime.strptime.return_value = whatever
, adalah yang kemudian, Anda harus langsung mengejek referensi strptime dalam file tempat tinggal metode yang diuji.Mock(return_value=datetime...)
pekerjaan.Untuk menambah solusi Daniel G :
Ini menciptakan kelas yang, ketika dipakai, akan mengembalikan objek datetime.date yang normal, tetapi yang juga bisa diubah.
sumber
date
/datetime
sendiri, ia menggunakan variabel yang tersedia secara global, jadi seharusnya tidak ada masalah: dpaste.com/790310Saya menghadapi situasi yang sama beberapa hari yang lalu, dan solusi saya adalah mendefinisikan fungsi dalam modul untuk diuji dan hanya mengejek itu:
Hari ini saya mengetahuinya FreezeGun , dan sepertinya menutupi kasus ini dengan indah
sumber
Cara termudah bagi saya adalah melakukan ini:
PERHATIAN untuk solusi ini: semua fungsi dari
datetime module
daritarget_module
akan berhenti bekerja.sumber
datetime_mock.now = Mock(return_value=datetime(1999, 1, 1)
itu bahkan bisa dipersingkat menjadidatetime_mock.now.return_value = datetime(1999, 1, 1)
. Alih-alih memulai tambalan denganstart()
, pertimbangkan untuk menggunakanwith patch(...):
manajer konteks untuk memastikan bahwadatetime
berperilaku teratur (tidak terhalang) lagi ketika tes Anda berakhir.datetime.datetime.now()
unmocked ^^?datetime module
daritarget_module
akan berhenti bekerja.Anda dapat menggunakan pendekatan berikut, berdasarkan pada solusi Daniel G. Yang satu ini memiliki keuntungan tidak melanggar jenis memeriksa
isinstance(d, datetime.date)
.Pada dasarnya, kami mengganti
datetime.date
kelas berbasis-C dengan subclass python kami sendiri, yang menghasilkandatetime.date
instance asli dan menjawabisinstance()
pertanyaan persis seperti aslidatetime.date
.Gunakan itu sebagai manajer konteks dalam pengujian Anda:
Pendekatan serupa dapat digunakan untuk mengejek
datetime.datetime.now()
fungsi.sumber
__instancecheck__
metode ini.Secara umum, Anda akan memiliki
datetime
atau mungkindatetime.date
diimpor ke modul di suatu tempat. Cara yang lebih efektif untuk memperolok metode ini adalah dengan menambalnya pada modul yang mengimpornya. Contoh:a.py
Kemudian untuk pengujian Anda, objek tiruan itu sendiri akan diteruskan sebagai argumen ke metode pengujian. Anda akan mengatur tiruan dengan nilai hasil yang Anda inginkan, dan kemudian memanggil metode Anda dalam pengujian. Maka Anda akan menegaskan bahwa metode Anda melakukan apa yang Anda inginkan.
Sebuah kata peringatan. Sangat mungkin untuk pergi ke laut dengan mengejek. Ketika Anda melakukannya, itu membuat tes Anda lebih lama, lebih sulit untuk dipahami, dan tidak mungkin untuk dipertahankan. Sebelum Anda mengejek metode sesederhana
datetime.date.today
, tanyakan pada diri Anda apakah Anda benar - benar perlu mengejeknya. Jika pengujian Anda singkat dan langsung ke sasaran dan berfungsi dengan baik tanpa mengejek fungsi, Anda mungkin hanya melihat detail internal kode yang Anda uji daripada objek yang perlu Anda tiru.sumber
Berikut cara lain untuk mengejek
datetime.date.today()
dengan bonus tambahan yang sisadatetime
fungsi terus bekerja, karena objek tiruan dikonfigurasi untuk membungkusdatetime
modul asli :Perhatikan
wraps=datetime
argumen untukmock.patch()
- ketikafoo_module
menggunakandatetime
fungsi lain selaindate.today()
mereka akan diteruskan kedatetime
modul yang dibungkus asli .sumber
Beberapa solusi dibahas di http://blog.xelnor.net/python-mocking-datetime/ . Singkatnya:
Objek tiruan - Sederhana dan efisien tetapi merusak isinstance () memeriksa:
Kelas tiruan
Digunakan sebagai:
sumber
Mungkin Anda bisa menggunakan metode "today ()" Anda sendiri yang akan Anda tempel di tempat yang diperlukan. Contoh dengan mengejek utcnow () dapat ditemukan di sini: https://bitbucket.org/k_bx/blog/src/tip/source/en_posts/2012-07-13-double-call-hack.rst?at=default
sumber
Saya menerapkan metode @ user3016183 menggunakan dekorator khusus:
Saya pikir itu mungkin membantu seseorang suatu hari ...
sumber
Dimungkinkan untuk mengejek fungsi
datetime
modul tanpa menambahkanside_effects
sumber
Bagi Anda yang menggunakan pytest dengan mocker di sini adalah bagaimana saya mengejek
datetime.datetime.now()
yang sangat mirip dengan pertanyaan aslinya.Pada dasarnya tiruan harus diatur untuk mengembalikan tanggal yang ditentukan. Anda tidak dapat menambal objek datetime secara langsung.
sumber
Saya membuat pekerjaan ini dengan mengimpor
datetime
sebagairealdatetime
dan mengganti metode yang saya butuhkan di tiruan dengan metode nyata:sumber
Anda dapat mengejek
datetime
menggunakan ini:Dalam modul
sources.py
:Di Anda
tests.py
:sumber
sources
di dekorator Anda?CPython benar-benar mengimplementasikan modul datetime menggunakan Lib / datetime.py -Python murni dan Modul C-dioptimalkan / _datetimemodule.c . Versi C-dioptimalkan tidak dapat ditambal tetapi versi pure-Python bisa.
Di bagian bawah implementasi pure-Python di Lib / datetime.py adalah kode ini:
Kode ini mengimpor semua definisi C-dioptimalkan dan secara efektif menggantikan semua definisi pure-Python. Kita bisa memaksa CPython untuk menggunakan implementasi murni-Python dari modul datetime dengan melakukan:
Dengan menetapkan
sys.modules["_datetime"] = None
, kami memberi tahu Python untuk mengabaikan modul yang dioptimalkan-C. Kemudian kami memuat kembali modul yang menyebabkan impor dari_datetime
gagal. Sekarang definisi pure-Python tetap dan dapat ditambal secara normal.Jika Anda menggunakan Pytest maka sertakan cuplikan di atas di conftest.py dan Anda dapat menambal
datetime
objek secara normal.sumber