Koneksi database tiruan python unittest

Anda mungkin ingin mengganti metode pada sebuah objek untuk memeriksa apakah metode tersebut dipanggil dengan argumen yang benar oleh bagian lain dari sistem

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')
<MagicMock name='method()' id='...'>

Setelah tiruan kami digunakan (

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True
_8 dalam contoh ini) ia memiliki metode dan atribut yang memungkinkan Anda membuat pernyataan tentang bagaimana ia digunakan

Catatan

Dalam sebagian besar contoh ini, kelas dan dapat dipertukarkan. Karena

>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
_0 adalah kelas yang lebih mumpuni, kelas ini masuk akal untuk digunakan secara default

Setelah tiruan dipanggil, atributnya disetel ke

>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
3. Lebih penting lagi kita dapat menggunakan metode or untuk memeriksa apakah itu dipanggil dengan argumen yang benar

Contoh ini menguji bahwa pemanggilan

>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
_6 menghasilkan panggilan ke metode
>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
7

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_

Mock untuk Memanggil Metode pada Objek

Pada contoh terakhir kami menambal metode langsung pada objek untuk memeriksa apakah metode tersebut dipanggil dengan benar. Kasus penggunaan umum lainnya adalah melewatkan objek ke dalam metode (atau beberapa bagian dari sistem yang diuji) dan kemudian memeriksa apakah itu digunakan dengan cara yang benar

>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
_8 sederhana di bawah ini memiliki metode
>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
9. Jika dipanggil dengan objek maka ia memanggil
>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
0 di atasnya

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...

Jadi untuk mengujinya kita perlu meneruskan objek dengan metode

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
0 dan memeriksa apakah itu dipanggil dengan benar

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()

Kami tidak perlu melakukan pekerjaan apa pun untuk menyediakan metode 'tutup' pada tiruan kami. Mengakses dekat membuatnya. Jadi, jika 'tutup' belum dipanggil maka mengaksesnya dalam pengujian akan membuatnya, tetapi akan memunculkan pengecualian kegagalan

Kelas Mengejek

Kasus penggunaan yang umum adalah untuk mengejek kelas yang dibuat oleh kode Anda yang sedang diuji. Saat Anda menambal suatu kelas, maka kelas itu diganti dengan tiruan. Instance dibuat dengan memanggil kelas. Ini berarti Anda mengakses "contoh tiruan" dengan melihat nilai kembalian dari kelas tiruan

Dalam contoh di bawah ini kita memiliki fungsi

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
_3 yang membuat
>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
4 dan memanggil metode di atasnya. Panggilan untuk mengganti kelas
>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
_4 dengan tiruan. Instance
>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
4 adalah hasil dari pemanggilan mock, sehingga dikonfigurasi dengan memodifikasi mock

>>> def some_function():
..     instance = module.Foo()
..     return instance.method()
...
>>> with patch('module.Foo') as mock:
..     instance = mock.return_value
..     instance.method.return_value = 'the result'
..     result = some_function()
..     assert result == 'the result'

Memberi nama tiruan Anda

Mungkin bermanfaat untuk memberi nama tiruan Anda. Nama ditampilkan di repr tiruan dan dapat membantu saat tiruan muncul di pesan kegagalan pengujian. Nama ini juga disebarkan ke atribut atau metode tiruan

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>

Melacak semua Panggilan

Seringkali Anda ingin melacak lebih dari satu panggilan ke suatu metode. Atribut merekam semua panggilan ke atribut turunan dari mock - dan juga ke turunannya

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]

Jika Anda membuat pernyataan tentang

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
_9 dan metode tak terduga telah dipanggil, maka pernyataan tersebut akan gagal. Ini berguna karena selain menegaskan bahwa panggilan yang Anda harapkan telah dilakukan, Anda juga memeriksa apakah panggilan dilakukan dalam urutan yang benar dan tanpa panggilan tambahan.

Anda menggunakan objek untuk menyusun daftar untuk dibandingkan dengan

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
9

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True
_

Namun, parameter ke panggilan yang mengembalikan tiruan tidak direkam, yang berarti tidak mungkin melacak panggilan bersarang di mana parameter yang digunakan untuk membuat leluhur penting

>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True

Menetapkan Nilai dan Atribut Pengembalian

Mengatur nilai pengembalian pada objek tiruan sangatlah mudah

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
_

Tentu saja Anda dapat melakukan hal yang sama untuk metode pada mock

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_0

Nilai pengembalian juga dapat diatur dalam konstruktor

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_1

Jika Anda memerlukan pengaturan atribut pada tiruan Anda, lakukan saja

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_2

Terkadang Anda ingin meniru situasi yang lebih kompleks, seperti misalnya

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
03. Jika kami ingin panggilan ini mengembalikan daftar, maka kami harus mengonfigurasi hasil panggilan bersarang

Kita dapat menggunakan untuk membuat kumpulan panggilan dalam "panggilan berantai" seperti ini untuk penegasan yang mudah setelahnya

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_3

Ini adalah panggilan ke

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_05 yang mengubah objek panggilan kita menjadi daftar panggilan yang mewakili panggilan berantai

Meningkatkan pengecualian dengan tiruan

Atribut yang berguna adalah. Jika Anda menyetel ini ke kelas atau instance pengecualian, maka pengecualian akan dimunculkan saat mock dipanggil

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_4

Fungsi efek samping dan iterables

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_06 juga dapat diatur ke fungsi atau iterable. Kasus penggunaan untuk
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
06 sebagai iterable adalah di mana tiruan Anda akan dipanggil beberapa kali, dan Anda ingin setiap panggilan mengembalikan nilai yang berbeda. Saat Anda menyetel
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
06 ke iterable, setiap panggilan ke tiruan mengembalikan nilai berikutnya dari iterable

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_5

Untuk kasus penggunaan yang lebih lanjut, seperti secara dinamis memvariasikan nilai kembalian tergantung pada apa yang disebut mock,

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
06 dapat berupa fungsi. Fungsi akan dipanggil dengan argumen yang sama dengan mock. Apa pun fungsi yang dikembalikan, itulah yang dikembalikan oleh panggilan

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_6

Mengejek iterator asinkron

Sejak Python 3. 8,

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
11 dan
>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
0 memiliki dukungan untuk mengejek melalui
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
13. Atribut
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_13 dapat digunakan untuk mengatur nilai pengembalian yang akan digunakan untuk iterasi

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_7

Mengejek manajer konteks asinkron

Sejak Python 3. 8,

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
11 dan
>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
0 memiliki dukungan untuk mengejek melalui
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
18 dan
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
19. Secara default,
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_18 dan
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
19 adalah
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
11 instance yang mengembalikan fungsi asinkron

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_8

Membuat Mock dari Objek yang Ada

Satu masalah dengan penggunaan ejekan yang berlebihan adalah ia memasangkan pengujian Anda dengan implementasi ejekan Anda daripada kode asli Anda. Misalkan Anda memiliki kelas yang mengimplementasikan

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
23. Dalam pengujian untuk kelas lain, Anda memberikan tiruan dari objek ini yang juga menyediakan
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
23. Jika nanti Anda refactor kelas pertama, sehingga tidak lagi memiliki
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
23 - maka tes Anda akan terus lulus meskipun kode Anda sekarang rusak

memungkinkan Anda menyediakan objek sebagai spesifikasi untuk mock, menggunakan argumen kata kunci spec. Mengakses metode/atribut pada tiruan yang tidak ada pada objek spesifikasi Anda akan segera memunculkan kesalahan atribut. Jika Anda mengubah penerapan spesifikasi Anda, maka pengujian yang menggunakan kelas tersebut akan langsung gagal tanpa Anda harus membuat instance kelas dalam pengujian tersebut

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_9

Menggunakan spesifikasi juga memungkinkan pencocokan panggilan yang lebih cerdas ke mock, terlepas dari apakah beberapa parameter diteruskan sebagai argumen posisi atau bernama

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_0

Jika Anda ingin pencocokan yang lebih cerdas ini juga berfungsi dengan pemanggilan metode pada tiruan, Anda dapat menggunakan

Jika Anda menginginkan bentuk spesifikasi yang lebih kuat yang mencegah pengaturan atribut arbitrer serta mendapatkannya, maka Anda dapat menggunakan spec_set alih-alih spec

Dekorator Tambalan

Catatan

Penting bagi Anda untuk menambal objek di namespace tempat mereka dicari. Ini biasanya mudah, tetapi untuk membaca panduan cepat

Kebutuhan umum dalam pengujian adalah untuk menambal atribut kelas atau atribut modul, misalnya menambal builtin atau menambal kelas dalam modul untuk menguji apakah itu dipakai. Modul dan kelas secara efektif bersifat global, jadi penambalan pada mereka harus dibatalkan setelah pengujian atau tambalan akan tetap ada pada pengujian lain dan menyebabkan masalah yang sulit didiagnosis

mock menyediakan tiga dekorator yang nyaman untuk ini. , dan.

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_31 mengambil string tunggal, dalam bentuk
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
32 untuk menentukan atribut yang Anda tambal. Ini juga secara opsional mengambil nilai yang Anda inginkan untuk diganti dengan atribut (atau kelas atau apa pun). 'tambalan. object’ mengambil sebuah objek dan nama atribut yang ingin Anda tambal, plus secara opsional nilai untuk menambalnya

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_33

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_1

Jika Anda menambal modul (termasuk ) maka gunakan sebagai gantinya

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_2

Nama modul dapat berupa 'titik-titik', dalam bentuk

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
37 jika diperlukan

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_3

Pola yang bagus adalah dengan menghiasi metode pengujian itu sendiri

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_4

Jika Anda ingin menambal dengan Mock, Anda dapat menggunakan hanya dengan satu argumen (atau dengan dua argumen). Mock akan dibuat untuk Anda dan diteruskan ke fungsi/metode pengujian

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_5

Anda dapat menumpuk beberapa dekorator tambalan menggunakan pola ini

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_6

Saat Anda menumpuk dekorator tambalan, tiruan diteruskan ke fungsi yang didekorasi dengan urutan yang sama dengan yang diterapkan (urutan Python normal yang diterapkan dekorator). Ini berarti dari bawah ke atas, jadi pada contoh di atas tiruan untuk

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
40 diteruskan terlebih dahulu

Ada juga untuk mengatur nilai dalam kamus hanya selama cakupan dan mengembalikan kamus ke keadaan semula saat tes berakhir

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_7

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_31,
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
33 dan
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
44 semua dapat digunakan sebagai manajer konteks

Di mana Anda gunakan untuk membuat tiruan untuk Anda, Anda bisa mendapatkan referensi ke tiruan menggunakan bentuk "as" dari pernyataan with

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_8

Sebagai alternatif

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_31,
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
33 dan
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
44 dapat digunakan sebagai dekorator kelas. Ketika digunakan dengan cara ini sama dengan menerapkan dekorator secara individual ke setiap metode yang namanya dimulai dengan "test"

Contoh Selanjutnya

Berikut adalah beberapa contoh lagi untuk beberapa skenario yang sedikit lebih maju

Mengejek panggilan berantai

Mengejek panggilan berantai sebenarnya mudah dengan tiruan setelah Anda memahami atributnya. Ketika tiruan dipanggil untuk pertama kali, atau Anda mengambil

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
8 sebelum dipanggil, yang baru dibuat

Ini berarti Anda dapat melihat bagaimana objek yang dikembalikan dari panggilan ke objek tiruan telah digunakan dengan menginterogasi tiruan

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
8

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_9

Dari sini adalah langkah sederhana untuk mengonfigurasi dan kemudian membuat pernyataan tentang panggilan berantai. Tentu saja alternatif lain adalah menulis kode Anda dengan cara yang lebih dapat diuji sejak awal…

Jadi, misalkan kita memiliki beberapa kode yang terlihat seperti ini

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
0

Dengan asumsi bahwa

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_53 sudah teruji dengan baik, bagaimana kita menguji
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
54?

Karena rangkaian panggilan ini dibuat dari atribut instance, kita dapat menambal atribut

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
56 pada instance
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
57. Dalam kasus khusus ini kami hanya tertarik pada nilai pengembalian dari panggilan terakhir ke
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
58 sehingga kami tidak memiliki banyak konfigurasi untuk dilakukan. Mari kita asumsikan objek yang dikembalikannya adalah 'seperti file', jadi kami akan memastikan bahwa objek respons kami menggunakan builtin sebagai
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
60

Untuk melakukan ini, kami membuat instance tiruan sebagai backend tiruan kami dan membuat objek respons tiruan untuknya. Untuk menyetel respons sebagai nilai pengembalian untuk

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
58 final itu, kita dapat melakukan ini

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
1

Kita dapat melakukannya dengan cara yang sedikit lebih baik menggunakan metode untuk secara langsung menetapkan nilai pengembalian untuk kita

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
2

Dengan ini kita monyet menambal "backend tiruan" di tempatnya dan dapat melakukan panggilan yang sebenarnya

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
3

Dengan menggunakan kita dapat memeriksa panggilan berantai dengan satu penegasan. Panggilan berantai adalah beberapa panggilan dalam satu baris kode, sehingga akan ada beberapa entri dalam

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
9. Kita dapat menggunakan untuk membuat daftar panggilan ini untuk kita

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
4

Mengejek sebagian

Dalam beberapa pengujian saya ingin mengejek panggilan untuk mengembalikan tanggal yang diketahui, tetapi saya tidak ingin mencegah kode yang diuji membuat objek tanggal baru. Sayangnya ditulis dalam C, jadi saya tidak bisa begitu saja menambal metode statis

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
68

Saya menemukan cara sederhana untuk melakukan ini yang melibatkan secara efektif membungkus kelas tanggal dengan tiruan, tetapi meneruskan panggilan ke konstruktor ke kelas nyata (dan mengembalikan contoh nyata)

The digunakan di sini untuk mengejek kelas

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
70 dalam modul yang diuji. Atribut
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
06 pada kelas tanggal tiruan kemudian disetel ke fungsi lambda yang mengembalikan tanggal sebenarnya. Saat kelas tanggal tiruan dipanggil, tanggal sebenarnya akan dibuat dan dikembalikan oleh
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
06

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
5

Perhatikan bahwa kami tidak menambal secara global, kami menambal

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
70 dalam modul yang menggunakannya. Lihat

Ketika

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_68 dipanggil, tanggal yang diketahui dikembalikan, tetapi panggilan ke konstruktor
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
76 masih mengembalikan tanggal normal. Tanpa ini, Anda dapat mendapati diri Anda harus menghitung hasil yang diharapkan menggunakan algoritme yang persis sama dengan kode yang diuji, yang merupakan anti-pola pengujian klasik

Panggilan ke konstruktor tanggal dicatat dalam atribut

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
77 (
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
78 dan teman-teman) yang mungkin juga berguna untuk pengujian Anda

Cara alternatif untuk menangani tanggal yang mengejek, atau kelas bawaan lainnya, dibahas dalam entri blog ini

Mengejek Metode Generator

Generator Python adalah fungsi atau metode yang menggunakan pernyataan untuk mengembalikan serangkaian nilai saat diulang

Metode / fungsi generator dipanggil untuk mengembalikan objek generator. Ini adalah objek generator yang kemudian diulang. Metode protokol untuk iterasi adalah , jadi kita dapat menirunya menggunakan a

Berikut adalah contoh kelas dengan metode “iter” yang diimplementasikan sebagai generator

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
6

Bagaimana kita mengejek kelas ini, dan khususnya metode "iter"?

Untuk mengonfigurasi nilai yang dikembalikan dari iterasi (tersirat dalam panggilan ke ), kita perlu mengonfigurasi objek yang dikembalikan oleh panggilan ke

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
83

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
7

Ada juga ekspresi generator dan penggunaan generator yang lebih canggih, tetapi kami tidak membahasnya di sini. Pengantar yang sangat bagus untuk generator dan seberapa kuat mereka. Trik Generator untuk Pemrogram Sistem

Menerapkan tambalan yang sama untuk setiap metode pengujian

Jika Anda menginginkan beberapa tambalan untuk beberapa metode pengujian, cara yang jelas adalah menerapkan dekorator tambalan ke setiap metode. Ini bisa terasa seperti pengulangan yang tidak perlu. Sebagai gantinya, Anda dapat menggunakan (dalam berbagai bentuknya) sebagai dekorator kelas. Ini menerapkan tambalan ke semua metode pengujian di kelas. Metode pengujian diidentifikasi dengan metode yang namanya dimulai dengan

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
85

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
8

Cara alternatif untuk mengelola tambalan adalah dengan menggunakan. Ini memungkinkan Anda untuk memindahkan tambalan ke metode

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
86 dan
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
87 Anda

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()
_9

Jika Anda menggunakan teknik ini, Anda harus memastikan bahwa penambalan “dibatalkan” dengan menghubungi

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
88. Ini bisa lebih fiddlier dari yang Anda kira, karena jika pengecualian dimunculkan di setUp maka tearDown tidak dipanggil. membuat ini lebih mudah

>>> def some_function():
..     instance = module.Foo()
..     return instance.method()
...
>>> with patch('module.Foo') as mock:
..     instance = mock.return_value
..     instance.method.return_value = 'the result'
..     result = some_function()
..     assert result == 'the result'
0

Mengejek Metode Tidak Terikat

Saat menulis tes hari ini saya perlu menambal metode yang tidak terikat (menambal metode di kelas daripada di instance). Saya membutuhkan self untuk diteruskan sebagai argumen pertama karena saya ingin menegaskan tentang objek mana yang memanggil metode khusus ini. Masalahnya adalah Anda tidak dapat menambal dengan tiruan untuk ini, karena jika Anda mengganti metode tidak terikat dengan tiruan, itu tidak menjadi metode terikat saat diambil dari instance, sehingga tidak diteruskan sendiri. Solusinya adalah menambal metode tidak terikat dengan fungsi nyata sebagai gantinya. Dekorator membuatnya sangat mudah untuk menambal metode dengan tiruan sehingga harus membuat fungsi nyata menjadi gangguan

Jika Anda melewati

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_91 untuk menambal maka itu akan menambal dengan objek fungsi nyata. Objek fungsi ini memiliki tanda tangan yang sama dengan objek yang digantikannya, tetapi didelegasikan ke tiruan di bawah tenda. Anda masih membuat tiruan Anda secara otomatis dengan cara yang persis sama seperti sebelumnya. Apa artinya, adalah bahwa jika Anda menggunakannya untuk menambal metode tidak terikat pada kelas, fungsi yang diejek akan diubah menjadi metode terikat jika diambil dari sebuah instance. Itu akan
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_92 diteruskan sebagai argumen pertama, yang persis seperti yang saya inginkan

>>> def some_function():
..     instance = module.Foo()
..     return instance.method()
...
>>> with patch('module.Foo') as mock:
..     instance = mock.return_value
..     instance.method.return_value = 'the result'
..     result = some_function()
..     assert result == 'the result'
1

Jika kita tidak menggunakan

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_91 maka metode tidak terikat akan ditambal dengan instance Mock, dan tidak dipanggil dengan
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
92

Memeriksa beberapa panggilan dengan tiruan

mock memiliki API yang bagus untuk membuat pernyataan tentang bagaimana objek tiruan Anda digunakan

>>> def some_function():
..     instance = module.Foo()
..     return instance.method()
...
>>> with patch('module.Foo') as mock:
..     instance = mock.return_value
..     instance.method.return_value = 'the result'
..     result = some_function()
..     assert result == 'the result'
2

Jika tiruan Anda hanya dipanggil sekali, Anda dapat menggunakan metode

>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
5 yang juga menyatakan bahwa
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
78 adalah salah satu

>>> def some_function():
..     instance = module.Foo()
..     return instance.method()
...
>>> with patch('module.Foo') as mock:
..     instance = mock.return_value
..     instance.method.return_value = 'the result'
..     result = some_function()
..     assert result == 'the result'
3

Baik

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_97 dan
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
98 membuat pernyataan tentang panggilan terakhir. Jika tiruan Anda akan dipanggil beberapa kali, dan Anda ingin membuat pernyataan tentang semua panggilan yang dapat Anda gunakan

>>> def some_function():
..     instance = module.Foo()
..     return instance.method()
...
>>> with patch('module.Foo') as mock:
..     instance = mock.return_value
..     instance.method.return_value = 'the result'
..     result = some_function()
..     assert result == 'the result'
_4

Pembantu membuatnya mudah untuk membuat pernyataan tentang panggilan ini. Anda dapat membuat daftar panggilan yang diharapkan dan membandingkannya dengan

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
99. Ini terlihat sangat mirip dengan perwakilan dari
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
99

>>> def some_function():
..     instance = module.Foo()
..     return instance.method()
...
>>> with patch('module.Foo') as mock:
..     instance = mock.return_value
..     instance.method.return_value = 'the result'
..     result = some_function()
..     assert result == 'the result'
5

Mengatasi argumen yang bisa berubah

Situasi lain yang jarang terjadi, tetapi bisa menggigit Anda, adalah ketika tiruan Anda dipanggil dengan argumen yang bisa berubah.

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_03 dan
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
99 menyimpan referensi ke argumen. Jika argumen dimutasi oleh kode yang diuji maka Anda tidak dapat lagi membuat pernyataan tentang nilai-nilainya saat mock dipanggil

Berikut beberapa contoh kode yang menunjukkan masalahnya. Bayangkan fungsi-fungsi berikut didefinisikan dalam 'mymodule'

>>> def some_function():
..     instance = module.Foo()
..     return instance.method()
...
>>> with patch('module.Foo') as mock:
..     instance = mock.return_value
..     instance.method.return_value = 'the result'
..     result = some_function()
..     assert result == 'the result'
6

Saat kami mencoba menguji bahwa

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
05 memanggil
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
06 dengan argumen yang benar, lihat apa yang terjadi

>>> def some_function():
..     instance = module.Foo()
..     return instance.method()
...
>>> with patch('module.Foo') as mock:
..     instance = mock.return_value
..     instance.method.return_value = 'the result'
..     result = some_function()
..     assert result == 'the result'
7

Satu kemungkinan adalah tiruan untuk menyalin argumen yang Anda berikan. Ini kemudian dapat menyebabkan masalah jika Anda melakukan pernyataan yang mengandalkan identitas objek untuk kesetaraan

Inilah salah satu solusi yang menggunakan fungsi

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_06. Jika Anda menyediakan fungsi
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
06 untuk mock maka
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
06 akan dipanggil dengan argumen yang sama dengan mock. Ini memberi kita kesempatan untuk menyalin argumen dan menyimpannya untuk pernyataan selanjutnya. Dalam contoh ini saya menggunakan tiruan lain untuk menyimpan argumen sehingga saya bisa menggunakan metode tiruan untuk melakukan pernyataan. Sekali lagi fungsi pembantu mengatur ini untuk saya

>>> def some_function():
..     instance = module.Foo()
..     return instance.method()
...
>>> with patch('module.Foo') as mock:
..     instance = mock.return_value
..     instance.method.return_value = 'the result'
..     result = some_function()
..     assert result == 'the result'
8

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_10 dipanggil dengan tiruan yang akan dipanggil. Ini mengembalikan mock baru yang kita buat pernyataannya. Fungsi
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
06 membuat salinan args dan memanggil
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
12 kita dengan salinan

Catatan

Jika tiruan Anda hanya akan digunakan sekali, ada cara yang lebih mudah untuk memeriksa argumen pada saat mereka dipanggil. Anda cukup melakukan pengecekan di dalam fungsi

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
06

>>> def some_function():
..     instance = module.Foo()
..     return instance.method()
...
>>> with patch('module.Foo') as mock:
..     instance = mock.return_value
..     instance.method.return_value = 'the result'
..     result = some_function()
..     assert result == 'the result'
_9

Pendekatan alternatif adalah membuat subkelas dari or yang menyalin (menggunakan ) argumen. Berikut ini contoh penerapannya

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>
_0

Saat Anda membuat subkelas

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True
7 atau
>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
0 semua atribut yang dibuat secara dinamis, dan
>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
8 akan menggunakan subkelas Anda secara otomatis. Itu berarti semua anak dari
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_20 juga akan memiliki tipe
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
20

Tambalan Bersarang

Menggunakan tambalan sebagai pengelola konteks memang bagus, tetapi jika Anda melakukan beberapa tambalan, Anda dapat berakhir dengan pernyataan bersarang yang mengindentasi lebih jauh dan lebih jauh ke kanan

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>
_1

Dengan fungsi

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
22 unittest dan kita dapat mencapai efek yang sama tanpa lekukan bersarang. Metode bantuan sederhana,
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
23, menempatkan tambalan pada tempatnya dan mengembalikan tiruan yang dibuat untuk kita

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>
_2

Mengejek kamus dengan MagicMock

Anda mungkin ingin mengejek kamus, atau objek kontainer lainnya, merekam semua akses ke sana sambil tetap berperilaku seperti kamus

Kita dapat melakukan ini dengan , yang akan berperilaku seperti kamus, dan menggunakan untuk mendelegasikan akses kamus ke kamus yang sebenarnya berada di bawah kendali kita

Ketika

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
26 dan
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
27 metode
>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
0 kita dipanggil (akses kamus normal) maka
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
06 dipanggil dengan kunci (dan dalam kasus
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
30 nilainya juga). Kami juga dapat mengontrol apa yang dikembalikan

Setelah

>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
_0 telah digunakan, kita dapat menggunakan atribut seperti untuk menegaskan tentang bagaimana kamus digunakan

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>
_3

Catatan

Alternatif untuk menggunakan

>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
_0 adalah menggunakan
>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True
7 dan hanya memberikan metode ajaib yang Anda inginkan secara khusus

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>
_4

Opsi ketiga adalah menggunakan

>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
_0 tetapi meneruskan
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
36 sebagai argumen spec (atau spec_set) sehingga
>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
0 yang dibuat hanya memiliki metode sihir kamus yang tersedia

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>
_5

Dengan fungsi efek samping ini, ________10______38 akan berperilaku seperti kamus normal tetapi merekam akses. Bahkan menimbulkan a jika Anda mencoba mengakses kunci yang tidak ada

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>
_6

Setelah digunakan, Anda dapat membuat pernyataan tentang akses menggunakan metode dan atribut tiruan normal

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>
_7

Subkelas tiruan dan atributnya

Ada berbagai alasan mengapa Anda mungkin ingin membuat subkelas. Salah satu alasannya mungkin untuk menambahkan metode pembantu. Ini contoh konyol

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>
_8

Perilaku standar untuk instance

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True
_7 adalah bahwa atribut dan tiruan nilai kembalian memiliki tipe yang sama dengan tiruan tempat mereka diakses. Ini memastikan bahwa
>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True
_7 atribut adalah
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
43 dan
>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True
0 atribut adalah
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
45. Jadi, jika Anda melakukan subkelas untuk menambahkan metode pembantu, maka metode tersebut juga akan tersedia pada atribut dan nilai pengembalian tiruan dari instance subkelas Anda

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>
_9

Terkadang ini tidak nyaman. Misalnya, satu pengguna mensubklasifikasi tiruan untuk membuat adaptor Twisted. Menerapkan ini ke atribut juga sebenarnya menyebabkan kesalahan

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True
_7 (dalam semua rasa) menggunakan metode yang disebut
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
47 untuk membuat "sub-tiruan" ini untuk atribut dan mengembalikan nilai. Anda dapat mencegah subkelas Anda digunakan untuk atribut dengan mengganti metode ini. Tanda tangannya adalah dibutuhkan argumen kata kunci arbitrer (
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
48) yang kemudian diteruskan ke konstruktor tiruan

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]
_0

Pengecualian untuk aturan ini adalah tiruan yang tidak dapat dipanggil. Atribut menggunakan varian yang dapat dipanggil karena tiruan yang tidak dapat dipanggil tidak dapat memiliki metode yang dapat dipanggil

Mengejek impor dengan tambalan. dikt

Satu situasi di mana ejekan bisa jadi sulit adalah saat Anda memiliki impor lokal di dalam suatu fungsi. Ini lebih sulit untuk ditiru karena mereka tidak menggunakan objek dari namespace modul yang dapat kita tambal

Umumnya impor lokal harus dihindari. Mereka kadang-kadang dilakukan untuk mencegah ketergantungan melingkar, yang biasanya ada cara yang jauh lebih baik untuk memecahkan masalah (memfaktorkan ulang kode) atau untuk mencegah "biaya di muka" dengan menunda impor. Ini juga dapat diselesaikan dengan cara yang lebih baik daripada impor lokal tanpa syarat (simpan modul sebagai atribut kelas atau modul dan lakukan impor hanya pada penggunaan pertama)

Selain itu ada cara untuk menggunakan

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
38 untuk mempengaruhi hasil impor. Mengimpor mengambil objek dari kamus. Perhatikan bahwa ini mengambil objek, yang tidak harus berupa modul. Mengimpor modul untuk pertama kali menghasilkan objek modul yang diletakkan di
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
50, jadi biasanya ketika Anda mengimpor sesuatu, Anda mendapatkan modul kembali. Namun ini tidak perlu terjadi

Ini berarti Anda dapat menggunakan untuk sementara menempatkan tiruan di tempat. Impor apa pun saat tambalan ini aktif akan mengambil tiruannya. Ketika tambalan selesai (fungsi yang didekorasi keluar, badan pernyataan dengan selesai atau

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
54 dipanggil) maka apa pun yang ada sebelumnya akan dipulihkan dengan aman

Berikut adalah contoh yang mengolok-olok modul 'fooble'

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]
_1

Seperti yang Anda lihat

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
55 berhasil, tetapi saat keluar tidak ada 'fooble' yang tersisa di

Ini juga berfungsi untuk formulir

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
57

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]
_2

Dengan sedikit lebih banyak pekerjaan, Anda juga dapat meniru impor paket

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]
_3

Pelacakan urutan panggilan dan lebih sedikit pernyataan panggilan bertele-tele

Kelas memungkinkan Anda melacak urutan pemanggilan metode pada objek tiruan Anda melalui atribut. Ini tidak memungkinkan Anda untuk melacak urutan panggilan antara objek tiruan yang terpisah, namun kami dapat menggunakan untuk mencapai efek yang sama

Karena tiruan melacak panggilan ke tiruan anak di

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
9, dan mengakses atribut sewenang-wenang dari tiruan membuat tiruan anak, kita dapat membuat tiruan terpisah dari induknya. Panggilan ke tiruan anak tersebut kemudian akan direkam, secara berurutan, di
>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
9 induk

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]
_4

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]
_5

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]
_6

Kami kemudian dapat menegaskan tentang panggilan, termasuk pesanan, dengan membandingkan dengan atribut

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
9 pada tiruan manajer

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]
_7

Jika

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_31 sedang membuat, dan menempatkan tiruan Anda, maka Anda dapat melampirkannya ke tiruan manajer menggunakan metode ini. Setelah melampirkan panggilan akan direkam dalam
>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
9 dari manajer

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]
_8

Jika banyak panggilan telah dilakukan, tetapi Anda hanya tertarik pada urutan tertentu saja, alternatifnya adalah menggunakan metode ini. Ini membutuhkan daftar panggilan (dibangun dengan objek). Jika urutan panggilan itu masuk maka pernyataan berhasil

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]
_9

Meskipun panggilan berantai

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_70 bukan satu-satunya panggilan yang dilakukan ke mock, penegasan masih berhasil

Kadang-kadang tiruan mungkin memiliki beberapa panggilan, dan Anda hanya tertarik untuk menyatakan tentang beberapa panggilan itu. Anda bahkan mungkin tidak peduli dengan pesanan. Dalam hal ini Anda dapat meneruskan

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_71 ke
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
72

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True
_0

Pencocokan argumen yang lebih kompleks

Menggunakan konsep dasar yang sama seperti kita dapat mengimplementasikan pencocokan untuk melakukan pernyataan yang lebih kompleks pada objek yang digunakan sebagai argumen untuk mengolok-olok

Misalkan kita berharap beberapa objek diteruskan ke tiruan yang secara default membandingkan sama berdasarkan identitas objek (yang merupakan default Python untuk kelas yang ditentukan pengguna). Untuk menggunakan kita perlu memasukkan objek yang sama persis. Jika kita hanya tertarik pada beberapa atribut objek ini, maka kita dapat membuat pencocokan yang akan memeriksa atribut ini untuk kita

Anda dapat melihat dalam contoh ini bagaimana panggilan 'standar' ke

>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
97 tidak cukup

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True
_1

Fungsi perbandingan untuk kelas

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
_4 kita mungkin terlihat seperti ini

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True
_2

Dan objek pencocokan yang dapat menggunakan fungsi perbandingan seperti ini untuk operasi persamaannya akan terlihat seperti ini

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True
_3

Menyatukan semua ini

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True
_4

>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
_77 dibuat dengan fungsi bandingkan kami dan objek
>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3
4 yang ingin kami bandingkan. Dalam
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_97 metode kesetaraan
>>> class ProductionClass:
..     def closer(self, something):
..         something.close()
...
77 akan dipanggil, yang membandingkan objek yang dipanggil dengan mock dengan objek yang kita buat dengan matcher kita. Jika cocok maka
>>> class ProductionClass:
..     def method(self):
..         self.something(1, 2, 3)
..     def something(self, a, b, c):
..         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)
_97 lolos, dan jika tidak, maka dinaikkan

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True
_5

Dengan sedikit mengutak-atik, Anda dapat membuat fungsi perbandingan menaikkan secara langsung dan memberikan pesan kegagalan yang lebih berguna

Mulai dari versi 1. 5, pustaka pengujian Python PyHamcrest menyediakan fungsionalitas serupa, yang mungkin berguna di sini, dalam bentuk pencocokan kesetaraannya ()

Bagaimana cara mengejek database Python unittest?

Membuat kelas MockDB . kelas TestCase. Metode ini akan membantu kita "menyiapkan" database sementara dan "meruntuhkan" database tersebut di akhir. Metode SetUp() berjalan sebelum setiap pengujian dalam satu kelas atau kasus pengujian.

Bagaimana cara menguji koneksi database dengan Python?

Cara menghubungkan database MySQL dengan Python .
Instal modul konektor MySQL. Gunakan perintah pip untuk menginstal konektor MySQL Python. .
Impor modul konektor MySQL. .
Gunakan metode connect(). .
Gunakan metode kursor(). .
Gunakan metode eksekusi(). .
Ekstrak hasil menggunakan fetchall().
Tutup objek kursor dan koneksi

Bisakah unit test terhubung ke database?

Pengujian unit tidak boleh bergantung pada infrastruktur . Jika pengembang baru mengkloning proyek, mereka perlu menyiapkan database sebelum berhasil menjalankan pengujian unit. There's no way to test this function without a database connection available at the time of testing. If a new developer clones the project they will need to set up a database before they can successfully run the unit tests.

Bisakah basis data diejek untuk pengujian unit?

Ya, tentu saja. Karena kode kita yang berbicara dengan DB sebenarnya sudah diuji dengan cermat di kuliah sebelumnya. Jadi yang perlu kita lakukan hanyalah. pastikan DB tiruan mengimplementasikan antarmuka yang sama dengan DB asli. Maka semuanya akan bekerja dengan baik saat disatukan.