Mengejek bisa sulit dimengerti. Saat saya menguji kode yang telah saya tulis, saya ingin melihat apakah kode melakukan apa yang seharusnya dilakukan dari ujung ke ujung. Saya biasanya mulai berpikir tentang tes fungsional dan terintegrasi, di mana saya memasukkan input yang realistis dan mendapatkan hasil yang realistis. Saya mengakses setiap sistem nyata yang digunakan kode saya untuk memastikan interaksi antara sistem tersebut berfungsi dengan baik, menggunakan objek nyata dan panggilan API nyata. Meskipun jenis pengujian ini penting untuk memverifikasi bahwa sistem yang kompleks saling bekerja dengan baik, pengujian tersebut bukanlah yang kita inginkan dari pengujian unit Show
Tes unit adalah tentang menguji lapisan terluar dari kode. Pengujian integrasi diperlukan, tetapi pengujian unit otomatis yang kami jalankan tidak boleh mencapai kedalaman interaksi sistem tersebut. Ini berarti bahwa panggilan API apa pun dalam fungsi yang kami uji dapat dan harus ditiru. Kita harus mengganti setiap panggilan API nontrivial atau pembuatan objek dengan panggilan atau objek tiruan. Hal ini memungkinkan kami untuk menghindari penggunaan sumber daya yang tidak perlu, menyederhanakan contoh pengujian kami, dan mengurangi waktu pengoperasiannya. Pikirkan untuk menguji fungsi yang mengakses API HTTP eksternal. Daripada memastikan bahwa server pengujian tersedia untuk mengirimkan tanggapan yang benar, kita dapat meniru pustaka HTTP dan mengganti semua panggilan HTTP dengan panggilan palsu. Hal ini mengurangi kompleksitas dan dependensi pengujian, dan memberi kami kontrol yang tepat atas apa yang dikembalikan pustaka HTTP, yang mungkin sulit dicapai sebaliknya Apa yang kita maksud dengan mengejek?
Istilah mengejek banyak digunakan, tetapi dokumen ini menggunakan definisi berikut
Panggilan fungsi tiruan mengembalikan nilai yang telah ditentukan dengan segera, tanpa melakukan pekerjaan apa pun. Atribut dan metode objek tiruan didefinisikan sama seluruhnya dalam pengujian, tanpa membuat objek nyata atau melakukan pekerjaan apa pun. Fakta bahwa penulis tes dapat menentukan nilai kembalian dari setiap pemanggilan fungsi memberinya kekuatan yang luar biasa saat menguji, tetapi itu juga berarti bahwa dia perlu melakukan beberapa pekerjaan dasar untuk mengatur semuanya dengan benar.
Dalam Python, mengejek dilakukan melalui modul 0. Modul ini berisi sejumlah kelas dan fungsi yang berguna, yang paling penting adalah fungsi 1 (sebagai dekorator dan pengelola konteks) dan kelas 2. Mengejek dengan Python sebagian besar dilakukan melalui penggunaan dua komponen yang kuat iniApa yang TIDAK kita maksud dengan mengejek?
Pengembang menggunakan banyak objek atau modul "tiruan", yang merupakan pengganti lokal yang berfungsi penuh untuk layanan jaringan dan API. Misalnya, pustaka 3 adalah pustaka tiruan 4 yang menangkap semua panggilan API 4 dan memprosesnya secara lokal. Meskipun tiruan ini memungkinkan pengembang untuk menguji API eksternal secara lokal, mereka tetap membutuhkan pembuatan objek nyata. Ini bukan jenis ejekan yang tercakup dalam dokumen ini. Dokumen ini khusus tentang penggunaan objek 2 untuk mengelola sepenuhnya aliran kontrol dari fungsi yang diuji, yang memungkinkan pengujian kegagalan dan penanganan pengecualian dengan mudahBagaimana kita mengejek dengan Python?
Mengejek dengan Python dilakukan dengan menggunakan 1 untuk membajak fungsi API atau panggilan pembuatan objek. Ketika 1 memotong panggilan, ia mengembalikan objek 2 secara default. Dengan menyetel properti pada objek _2, Anda dapat meniru panggilan API untuk mengembalikan nilai apa pun yang Anda inginkan atau menaikkan 1
Prosedur keseluruhan adalah sebagai berikut
Jika tes Anda lulus, Anda sudah selesai. Jika tidak, Anda mungkin mengalami kesalahan pada fungsi yang sedang diuji, atau Anda mungkin salah menyiapkan respons 2 Anda. Selanjutnya, kita akan membahas lebih detail tentang alat yang Anda gunakan untuk membuat dan mengonfigurasi tiruantambalan
1 dapat digunakan sebagai dekorator untuk fungsi pengujian, mengambil string yang menamai fungsi yang akan ditambal sebagai argumen. Agar _1 menemukan fungsi yang akan ditambal, itu harus ditentukan menggunakan nama yang sepenuhnya memenuhi syarat, yang mungkin tidak seperti yang Anda harapkan. Jika kelas diimpor menggunakan pernyataan _6, 7 menjadi bagian dari namespace modul yang diimpor
Misalnya, jika kelas diimpor dalam modul 8 sebagai berikut
Itu harus ditambal sebagai 9, bukan 0, karena semantik pernyataan 1, yang mengimpor kelas dan fungsi ke namespace saat ini
Biasanya 1 digunakan untuk menambal panggilan API eksternal atau panggilan fungsi intensif waktu atau sumber daya lainnya atau pembuatan objek. Anda seharusnya hanya menambal beberapa panggilan per pengujian. Jika Anda mendapati diri Anda mencoba _1 lebih dari beberapa kali, pertimbangkan untuk memfaktorkan ulang pengujian Anda atau fungsi yang Anda uji
Menggunakan _1 dekorator akan secara otomatis mengirim argumen posisi ke fungsi yang Anda dekorasi (i. e. , fungsi pengujian Anda). Saat menambal beberapa fungsi, dekorator yang paling dekat dengan fungsi yang sedang didekorasi dipanggil terlebih dahulu, sehingga akan membuat argumen posisi pertama
Secara default, argumen ini adalah instance dari 2, yang merupakan objek tiruan default 0. Anda dapat menentukan perilaku fungsi yang ditambal dengan menyetel atribut pada instance 2 yang dikembalikan
MagicMock
2 objek menyediakan antarmuka tiruan sederhana yang memungkinkan Anda menyetel nilai kembalian atau perilaku lain dari panggilan fungsi atau pembuatan objek yang Anda tambal. Ini memungkinkan Anda untuk sepenuhnya menentukan perilaku panggilan dan menghindari pembuatan objek nyata, yang bisa memberatkan. Misalnya, jika kita menambal panggilan ke 9, panggilan pustaka HTTP, kita dapat menentukan respons terhadap panggilan tersebut yang akan dikembalikan saat panggilan API dibuat dalam fungsi yang sedang diuji, daripada memastikan bahwa server pengujian
Dua atribut terpenting dari instance 2 adalah 1 dan 2, keduanya memungkinkan kita untuk menentukan perilaku pengembalian panggilan yang ditambalreturn_value
Atribut 1 pada instance 2 yang diteruskan ke fungsi pengujian Anda memungkinkan Anda untuk memilih apa yang dikembalikan oleh callable yang ditambal. Dalam kebanyakan kasus, Anda ingin mengembalikan versi tiruan dari apa yang biasanya dikembalikan oleh callable. Ini bisa berupa JSON, iterable, nilai, turunan dari objek respons nyata, 2 berpura-pura menjadi objek respons, atau apa saja. Saat menambal objek, panggilan yang ditambal adalah panggilan pembuatan objek, jadi 1 dari 2 harus berupa objek tiruan, yang bisa jadi 2 lainnyaJika kode yang Anda uji adalah Pythonic dan melakukan pengetikan bebek daripada pengetikan eksplisit, menggunakan 2 sebagai objek respons bisa nyaman. Daripada bersusah payah membuat instance kelas yang sebenarnya, Anda dapat menentukan pasangan kunci-nilai atribut arbitrer di konstruktor 2 dan mereka akan diterapkan secara otomatis ke instance
Perhatikan bahwa argumen diteruskan ke _1, i. e. , 2, adalah 2 dan kami menetapkan 1 ke 2 lainnya. Saat mengejek, semuanya adalah 2Menentukan MagicMock
Sementara fleksibilitas _2 nyaman untuk mengejek kelas dengan cepat dengan persyaratan yang rumit, itu juga bisa menjadi kerugian. Secara default, 2 bertindak seolah-olah mereka memiliki atribut apa pun, bahkan atribut yang tidak Anda inginkan untuk mereka miliki. Dalam contoh di atas, kita mengembalikan objek 2 alih-alih objek 0. Namun, katakanlah kita telah membuat kesalahan dalam panggilan 1 dan menambal fungsi yang seharusnya mengembalikan objek 2 alih-alih objek 0. 2 yang kita kembalikan akan tetap bertindak seolah-olah memiliki semua atribut dari objek 2, meskipun kita bermaksud memodelkan objek 0. Hal ini dapat menyebabkan kesalahan pengujian yang membingungkan dan perilaku pengujian yang salah
Solusi untuk ini adalah dengan _7 2 saat membuatnya, menggunakan argumen kata kunci 7. 0. Ini menciptakan 2 yang hanya akan mengizinkan akses ke atribut dan metode yang ada di kelas tempat 2 ditentukan. Mencoba untuk mengakses atribut yang tidak ada di objek aslinya akan memunculkan 3, seperti halnya objek sebenarnya. Contoh sederhananya adalah
efek samping
Terkadang Anda ingin menguji apakah fungsi Anda menangani pengecualian dengan benar, atau apakah beberapa panggilan fungsi yang Anda tambal ditangani dengan benar. Anda dapat melakukannya menggunakan _2. Menyetel _2 ke pengecualian akan segera memunculkan pengecualian itu saat fungsi yang ditambal dipanggil
Menyetel _2 ke iterable akan mengembalikan item berikutnya dari iterable setiap kali fungsi yang ditambal dipanggil. Menyetel _2 ke nilai lain akan mengembalikan nilai tersebut
menegaskan_dipanggil_dengan
_8 menegaskan bahwa fungsi yang ditambal dipanggil dengan argumen yang ditentukan sebagai argumen untuk 8
Contoh lengkap
Dalam contoh ini, saya menguji fungsi 0 pada 1. Ini berarti panggilan API di 2 akan dilakukan dua kali, yang merupakan waktu yang tepat untuk menggunakan 3
Kode lengkap dari contoh ada di sini
Saya menambal dua panggilan dalam fungsi yang sedang diuji ( 4), satu ke 5 dan satu ke 6. Karena saya menambal dua panggilan, saya mendapatkan dua argumen untuk fungsi pengujian saya, yang saya panggil 7 dan 8. Keduanya adalah objek 2. Dalam keadaan default, mereka tidak berbuat banyak. Kita perlu menetapkan beberapa perilaku respons kepada mereka
Tes ini untuk memastikan fasilitas coba lagi berfungsi pada akhirnya, jadi saya akan menelepon pembaruan beberapa kali, dan melakukan beberapa panggilan ke 5 dan 6
Di sini saya menyiapkan _2 yang saya inginkan. Saya ingin semua panggilan ke _5 berfungsi (mengembalikan 4 kosong tidak masalah untuk pengujian ini), panggilan pertama ke 6 gagal dengan pengecualian, dan panggilan kedua ke 6 berfungsi. Kontrol halus atas perilaku semacam ini hanya mungkin dilakukan melalui ejekan
Setelah saya menyiapkan _2s, sisa pengujiannya mudah. Perilaku adalah. panggilan pertama ke _6 gagal, sehingga fasilitas coba lagi membungkus 9 harus menangkap kesalahan, dan semuanya harus berfungsi untuk kedua kalinya. Perilaku ini dapat diverifikasi lebih lanjut dengan memeriksa riwayat panggilan 8 dan 7Kesimpulan
Menggunakan objek tiruan dengan benar bertentangan dengan intuisi kita untuk membuat pengujian senyata dan selengkap mungkin, tetapi melakukannya memberi kita kemampuan untuk menulis pengujian mandiri yang berjalan cepat, tanpa ketergantungan. Ini memberi kita kekuatan untuk menguji penanganan pengecualian dan kasus tepi yang tidak mungkin untuk diuji. Yang terpenting, ini memberi kami kebebasan untuk memfokuskan upaya pengujian kami pada fungsionalitas kode kami, daripada kemampuan kami untuk menyiapkan lingkungan pengujian. Dengan berkonsentrasi pada pengujian apa yang penting, kami dapat meningkatkan cakupan pengujian dan meningkatkan keandalan kode kami, itulah sebabnya kami menguji di tempat pertama |