Apakah mengolok-olok melanggar prinsip Terbuka / Tertutup?


13

Beberapa waktu lalu saya membaca, pada jawaban Stack Overflow yang tidak dapat saya temukan, kalimat yang menjelaskan bahwa Anda harus menguji API publik, dan penulis mengatakan bahwa Anda harus menguji antarmuka. Penulis juga menjelaskan bahwa jika implementasi metode berubah, Anda tidak perlu memodifikasi kasus uji, karena melakukan ini akan memutus kontrak yang memastikan sistem yang sedang diuji berfungsi. Dengan kata lain, tes harus gagal jika metode tidak berhasil, tetapi bukan karena implementasinya berubah.

Ini menarik perhatian saya ketika kita berbicara tentang mengejek. Karena mengejek sangat bergantung pada panggilan ekspektasi dari sistem yang sedang diuji dependensi, tiruan sangat erat digabungkan dengan implementasi daripada antarmuka.

Ketika meneliti mock vs stub , beberapa artikel setuju bahwa stub harus digunakan sebagai ganti tiruan , karena mereka tidak bergantung pada ekspektasi dari dependensi, yang berarti tes tidak memerlukan pengetahuan tentang sistem yang mendasari dalam implementasi pengujian.

Pertanyaan saya adalah:

  1. Apakah mengolok-olok melanggar prinsip terbuka / tertutup?
  2. Apakah ada sesuatu yang hilang dalam argumen yang mendukung bertopik pada paragraf terakhir, yang membuat bertopik tidak begitu hebat vs mengejek?
  3. Jika demikian, kapan kasus penggunaan yang baik untuk mengejek dan kapan kasus penggunaan yang baik untuk menggunakan bertopik?


8
Since mocking relays heavily on expectation calls from system under test's dependencies...Saya pikir ini adalah di mana Anda akan serba salah. Mock adalah representasi buatan dari sistem eksternal. Itu tidak mewakili sistem eksternal dengan cara apa pun, kecuali sejauh ia mensimulasikan sistem eksternal sedemikian rupa sehingga memungkinkan pengujian dijalankan terhadap kode yang memiliki ketergantungan pada sistem eksternal tersebut. Anda masih akan memerlukan tes integrasi untuk membuktikan bahwa kode Anda berfungsi dengan sistem yang nyata, yang tidak di-mocking.
Robert Harvey

8
Dengan kata lain, mock adalah implementasi pengganti. Itulah sebabnya kami memprogram ke antarmuka di tempat pertama, sehingga kami bisa menggunakan mock sebagai pengganti untuk implementasi nyata. Dengan kata lain, tiruan dipisahkan dari, bukan digabungkan ke, implementasi yang sebenarnya.
Robert Harvey

3
"Dengan kata lain, sebuah tes harus gagal jika metode tidak bekerja, tetapi bukan karena implementasinya berubah", ini tidak selalu benar. Ada banyak keadaan di mana Anda harus mengubah implementasi dan tes Anda.
whatsisname

Jawaban:


4
  1. Saya tidak mengerti mengapa mengolok-olok akan melanggar prinsip terbuka / tertutup. Jika Anda dapat menjelaskan kepada kami mengapa menurut Anda itu mungkin, maka kami mungkin dapat meredakan kekhawatiran Anda.

  2. Satu-satunya kekurangan bertopik yang dapat saya pikirkan adalah bahwa mereka umumnya memerlukan lebih banyak pekerjaan untuk menulis daripada mengejek, karena masing-masing dari mereka sebenarnya merupakan implementasi alternatif dari antarmuka dependen, sehingga umumnya harus menyediakan yang lengkap (atau meyakinkan lengkap) implementasi antarmuka dependen. Untuk memberikan Anda contoh ekstrem, jika subsistem Anda yang sedang diuji meminta RDBMS, maka tiruan dari RDBMS hanya akan menanggapi pertanyaan spesifik yang diketahui dikeluarkan oleh subsistem yang diuji, menghasilkan set data uji yang telah ditentukan. Di sisi lain, implementasi alternatif akan menjadi RDBMS dalam memori penuh, mungkin dengan beban tambahan karena harus meniru kebiasaan klien-server RDBMS aktual yang Anda gunakan pada produksi. (Untungnya, kita memiliki hal-hal seperti HSQLDB, jadi kita sebenarnya bisa melakukan itu, tapi tetap saja,

  3. Casing penggunaan yang baik untuk mengejek adalah ketika antarmuka bergantung terlalu rumit untuk menulis implementasi alternatif untuk itu, atau jika Anda yakin Anda hanya akan menulis tiruan sekali dan tidak pernah menyentuhnya lagi. Dalam kasus ini, silakan dan gunakan mock cepat dan kotor. Akibatnya, kasus penggunaan yang baik untuk bertopik (implementasi alternatif) adalah hampir semua yang lain. Terutama jika Anda melihat terlibat dalam hubungan jangka panjang dengan subsistem yang diuji, pasti pergi dengan implementasi alternatif yang akan bagus dan bersih, dan akan membutuhkan pemeliharaan hanya jika antarmuka berubah, alih-alih membutuhkan pemeliharaan setiap kali antarmuka berubah dan kapan saja implementasi subsistem yang diuji berubah.

PS Orang yang Anda maksud bisa saja saya, dalam salah satu jawaban terkait pengujian lainnya di sini di programmer.stackexchange.com, misalnya yang ini .


an alternative implementation would be a full-blown in-memory RDBMS- Anda tidak perlu harus sejauh itu dengan rintisan.
Robert Harvey

@ RobertTarvey dengan baik, dengan HSQLDB dan H2 tidak begitu sulit untuk benar-benar melangkah sejauh itu. Mungkin lebih sulit untuk melakukan sesuatu yang setengah-setengah agar tidak melangkah sejauh itu. Tetapi jika Anda memutuskan untuk melakukannya sendiri, Anda harus mulai dengan menulis parser SQL. Tentu, Anda dapat memotong beberapa sudut, tetapi ada banyak pekerjaan . Bagaimanapun, seperti yang saya katakan di atas, ini hanyalah contoh ekstrem.
Mike Nakis

9
  1. Prinsip Open / Closed sebagian besar tentang mampu mengubah perilaku kelas tanpa mengubahnya. Oleh karena itu, menyuntikkan ketergantungan komponen yang diejek di dalam kelas yang sedang diuji tidak melanggarnya.

  2. Masalah dengan tes ganda (mock / stub) adalah pada dasarnya Anda membuat asumsi arbitrer mengenai bagaimana kelas yang diuji berinteraksi dengan lingkungannya. Jika harapan itu salah, Anda mungkin akan mengalami beberapa masalah begitu kode tersebut digunakan. Jika Anda mampu membelinya, uji kode Anda dalam batasan yang sama dari yang membatasi lingkungan produksi Anda. Jika Anda tidak bisa, buat asumsi sesedikit mungkin, dan tirukan / rintisan hanya periferal sistem Anda (basis data, layanan otentikasi, klien HTTP, dll ...).

Satu-satunya alasan yang valid mengapa, IMHO, ganda harus digunakan, adalah ketika Anda perlu merekam interaksinya dengan kelas yang diuji, atau ketika Anda perlu memberikan data palsu (yang dapat dilakukan kedua teknik). Namun berhati-hatilah, menyalahgunakannya mencerminkan desain yang buruk, atau tes yang terlalu bergantung pada API yang sedang diimplementasikan.


6

Catatan: Saya berasumsi Anda mendefinisikan Mock berarti "kelas tanpa implementasi, hanya sesuatu yang dapat Anda monitor" dan Stub menjadi "parsial tiruan, alias menggunakan beberapa perilaku nyata dari kelas yang diimplementasikan", sesuai Stack ini Pertanyaan meluap .

Saya tidak yakin mengapa Anda berpikir bahwa konsensus adalah untuk menggunakan bertopik, misalnya itu justru sebaliknya dalam Dokumentasi Mockito

Seperti biasa Anda akan membaca peringatan tiruan parsial: Pemrograman berorientasi objek lebih sedikit mengatasi kompleksitas dengan membagi kompleksitas menjadi objek SRPy yang terpisah, spesifik. Bagaimana tiruan sebagian cocok dengan paradigma ini? Yah, itu tidak ... Partial mock biasanya berarti bahwa kompleksitas telah dipindahkan ke metode berbeda pada objek yang sama. Dalam kebanyakan kasus, ini bukan cara Anda ingin merancang aplikasi Anda.

Namun, ada beberapa kasus yang jarang terjadi ketika tiruan parsial berguna: berurusan dengan kode yang tidak dapat Anda ubah dengan mudah (antarmuka pihak ke-3, refactoring sementara kode legacy dll.) kode yang dirancang.

Dokumentasi itu mengatakan lebih baik daripada yang saya bisa. Menggunakan tiruan memungkinkan Anda untuk hanya menguji satu kelas tertentu, dan tidak ada yang lain; jika Anda membutuhkan tiruan parsial untuk mencapai perilaku yang Anda cari, Anda mungkin telah melakukan sesuatu yang salah, melanggar SRP, dan sebagainya, dan kode Anda bisa tahan. Mengolok-olok tidak melanggar prinsip buka-tutup, karena mereka hanya pernah digunakan dalam tes, mereka bukan perubahan nyata pada kode itu. Biasanya mereka dihasilkan dengan cepat oleh perpustakaan seperti cglib.


2
Dari pertanyaan SO yang disediakan sama (jawaban yang diterima), ini adalah definisi Mock / Stub yang saya maksudkan juga: Objek tiruan digunakan untuk mendefinisikan harapan yaitu: Dalam skenario ini saya mengharapkan metode A () dipanggil dengan parameter ini dan itu. Mock merekam dan memverifikasi harapan tersebut. Rintisan bertopik, di sisi lain memiliki tujuan yang berbeda: mereka tidak mencatat atau memverifikasi harapan, melainkan memungkinkan kita untuk "mengganti" perilaku, keadaan objek "palsu" untuk memanfaatkan skenario pengujian ...
Christopher Francisco

2

Saya pikir masalah mungkin timbul dari asumsi bahwa satu-satunya tes yang valid adalah mereka yang memenuhi tes terbuka / tertutup.

Sangat mudah untuk melihat bahwa satu-satunya pengujian yang seharusnya penting adalah yang menguji antarmuka. Namun, pada kenyataannya, seringkali lebih efektif untuk menguji antarmuka itu dengan menguji inner.

Misalnya, hampir tidak mungkin untuk menguji persyaratan negatif, seperti "implementasi tidak akan membuang pengecualian." Pertimbangkan antarmuka peta yang diimplementasikan dengan hashmap. Anda ingin memastikan bahwa hashmap memenuhi antarmuka peta, tanpa membuang, bahkan ketika harus mengulang hal-hal (yang bisa menjadi tidak pasti). Anda bisa menguji setiap kombinasi input untuk memastikan mereka memenuhi persyaratan antarmuka, tetapi itu bisa memakan waktu lebih lama daripada kematian panas alam semesta. Sebaliknya, Anda memecahkan enkapsulasi sedikit, dan mengembangkan tiruan yang berinteraksi lebih erat, memaksa hashmap untuk melakukan persis pengulangan yang diperlukan untuk memastikan algoritma pengulangan tidak melempar.

T / Dr: melakukannya "dengan buku" itu bagus, tetapi ketika dorongan datang untuk mendorong, memiliki produk di meja atasan Anda pada hari Jumat lebih berguna daripada test suite yang dibutuhkan hingga kematian panas dari semesta untuk mengkonfirmasi kesesuaian.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.