Dari sudut pandang TDD, apakah saya orang jahat jika saya menguji dengan endpoint langsung bukan tiruan?

16

Saya mengikuti TDD dengan religius. Proyek saya biasanya memiliki cakupan pengujian 85% atau lebih baik, dengan kasus uji yang bermakna.

Saya melakukan banyak pekerjaan dengan HBase , dan antarmuka klien utama, HTable, sangat menyebalkan. Diperlukan 3 atau 4 kali lebih lama untuk menulis tes unit saya daripada menulis tes yang menggunakan titik akhir langsung.

Saya tahu bahwa, secara filosofis, tes yang menggunakan ejekan harus lebih diprioritaskan daripada tes yang menggunakan titik akhir langsung. Tapi mengejek HTable adalah rasa sakit yang serius, dan saya tidak begitu yakin ini menawarkan banyak keuntungan dibandingkan pengujian terhadap contoh HBase langsung.

Semua orang di tim saya menjalankan instance HBase node tunggal di workstation mereka, dan kami memiliki instance HBase node tunggal yang berjalan di kotak Jenkins kami, jadi ini bukan masalah ketersediaan. Tes titik akhir langsung jelas membutuhkan waktu lebih lama untuk dijalankan daripada tes yang menggunakan ejekan, tetapi kami tidak terlalu peduli soal itu.

Saat ini, saya menulis tes titik akhir langsung DAN tes berbasis mock untuk semua kelas saya. Saya ingin membuang tiruannya, tetapi saya tidak ingin kualitasnya menurun.

Apa yang kalian pikirkan?

ketenangan dlm menghadapi bahaya
sumber
8
Titik akhir langsung sebenarnya bukan unit test, bukan? Ini tes integrasi. Tetapi pada akhirnya itu mungkin masalah pragmatisme; Anda dapat menghabiskan waktu menulis tiruan, atau menghabiskan waktu menulis fitur atau memperbaiki bug.
Robert Harvey
4
Saya telah mendengar cerita tentang orang-orang yang menghentikan layanan pihak ke-3 dengan menjalankan uji unit terhadap kode mereka sendiri ... yang dikaitkan dengan titik akhir langsung. Pembatasan nilai bukanlah sesuatu yang biasanya dilakukan atau diperhatikan oleh unit test.
14
Anda bukan orang jahat. Anda adalah orang baik yang melakukan hal buruk.
Kyralessa
15
Saya mengikuti TDD dengan agama. Mungkin itu masalahnya? Saya tidak berpikir salah satu dari metodologi ini dimaksudkan untuk diambil yang serius. ;)
FrustratedWithFormsDesigner
9
Mengikuti TDD secara religius berarti Anda membuang 15% kode yang tidak tertutup.
mouviciel

Jawaban:

23
  • Rekomendasi pertama saya adalah untuk tidak mengejek tipe yang tidak Anda miliki . Anda menyebut HTable sangat menyebalkan - mungkin Anda harus membungkusnya di Adaptor yang memperlihatkan 20% fitur HTable yang Anda butuhkan, dan mengejek pembungkus jika diperlukan.

  • Yang sedang berkata, mari kita asumsikan kita berbicara tentang tipe Anda semua miliki. Jika pengujian berbasis tiruan Anda berfokus pada skenario jalur bahagia di mana semuanya berjalan lancar, Anda tidak akan kehilangan apa pun karena mereka gagal. Tes integrasi Anda mungkin sudah menguji jalur yang sama persis.

    Namun, tes terisolasi menjadi menarik ketika Anda mulai berpikir tentang bagaimana sistem Anda sedang diuji harus bereaksi terhadap setiap hal kecil yang dapat terjadi seperti yang didefinisikan dalam kontrak kolaboratornya, terlepas dari objek konkret yang sebenarnya diajak bicara. Itulah bagian dari apa yang disebut beberapa kebenaran mendasar . Mungkin ada banyak kasus kecil dan banyak kombinasi lainnya. Di sinilah tes integrasi mulai menjadi buruk sementara tes terisolasi akan tetap cepat dan dikelola.

    Agar lebih konkret, apa yang terjadi jika salah satu metode adaptor HTable Anda mengembalikan daftar kosong? Bagaimana jika mengembalikan nol? Bagaimana jika itu melempar pengecualian koneksi? Ini harus didefinisikan dalam kontrak Adaptor jika hal-hal itu dapat terjadi, dan konsumen mana pun harus siap untuk menghadapi situasi ini , maka diperlukan pengujian untuk mereka.

Singkatnya: Anda tidak akan melihat penurunan kualitas dengan menghapus tes berbasis tiruan Anda jika mereka menguji hal-hal yang sama persis seperti tes integrasi Anda . Namun, mencoba membayangkan tes terisolasi tambahan (dan tes kontrak ) dapat membantu Anda memikirkan antarmuka / kontrak Anda secara luas dan meningkatkan kualitas dengan mengatasi cacat yang sulit dipikirkan dan / atau lambat untuk diuji dengan tes integrasi.

guillaume31
sumber
+1 Saya merasa jauh lebih mudah untuk membangun case edge dengan tes tiruan daripada mengisi database dengan case tersebut.
Rob
Saya setuju dengan sebagian besar jawaban Anda. Namun, saya tidak yakin saya setuju tentang bagian Adaptor. HTable adalah rasa sakit untuk diejek karena itu cukup bare-metal. Misalnya, jika Anda ingin melakukan operasi batch batch, Anda harus membuat banyak objek Get, menaruhnya di daftar, lalu panggil HTable.batch (). Dari perspektif mengejek, ini adalah rasa sakit yang serius, karena Anda harus membuat Pencocokan khusus yang memeriksa daftar objek dapatkan yang Anda lewati ke HTable.batch () dan kemudian mengembalikan hasil yang benar untuk daftar objek get () tersebut. Rasa sakit SERIUS.
sangfroid
Saya kira saya bisa membuat kelas pembungkus yang bagus dan ramah untuk HTable yang mengurus semua urusan rumah tangga itu, tetapi pada saat itu ... Saya merasa seperti sedang membangun kerangka kerja di sekitar HTable, dan haruskah itu benar-benar pekerjaan saya? Biasanya "Ayo bangun kerangka kerja!" adalah tanda saya akan ke arah yang salah. Saya bisa menghabiskan waktu berhari-hari menulis kelas untuk membuat HBase lebih ramah, dan saya tidak tahu apakah itu menggunakan waktu saya. Plus, kemudian saya menggunakan antarmuka atau pembungkus bukan hanya objek HTable biasa, dan itu pasti akan membuat kode saya lebih kompleks.
sangfroid
Tapi saya setuju pada poin utama Anda, bahwa orang tidak seharusnya menulis ejekan untuk kelas yang tidak mereka miliki. Dan tentu saja setuju tidak ada gunanya menulis tes tiruan yang menguji hal yang sama dengan tes integrasi. Tampaknya mengejek adalah yang terbaik untuk menguji antarmuka / kontrak. Terima kasih atas sarannya - ini sangat membantu saya!
sangfroid
Saya tidak tahu apa sebenarnya HTable dan bagaimana Anda menggunakannya, jadi jangan ambil contoh pembungkus saya untuk surat itu. Saya telah menyebutkan bungkusnya / adaptor karena saya pikir bungkusnya relatif kecil. Anda tidak perlu memperkenalkan replika satu-ke-satu ke HTable, yang tentu saja akan menyebalkan, apalagi seluruh kerangka kerja - tetapi Anda perlu jahitan , antarmuka antara bidang aplikasi Anda dan HTable. Seharusnya ulang kata-kata beberapa fungsi HTable ke dalam persyaratan aplikasi Anda sendiri. Pola Repositori adalah inkarnasi sempurna dari jahitan seperti itu ketika datang ke akses data.
guillaume31
11

Secara filosofis, tes yang menggunakan ejekan harus diprioritaskan daripada tes yang menggunakan titik akhir langsung

Saya pikir setidaknya, itulah titik kontroversi yang sedang berlangsung saat ini di antara para pendukung TDD.

Pandangan pribadi saya lebih dari itu untuk mengatakan bahwa tes berbasis mock sebagian besar merupakan cara untuk mewakili bentuk kontrak antarmuka ; idealnya rusak (yaitu gagal) jika dan hanya jika Anda mengubah antarmuka . Dan dengan demikian, dalam bahasa yang diketik dengan cukup kuat seperti Java, dan ketika menggunakan antarmuka yang didefinisikan secara eksplisit, itu hampir seluruhnya berlebihan: kompiler akan memberi tahu Anda jika Anda telah mengubah antarmuka.

Pengecualian utama adalah ketika Anda menggunakan antarmuka yang sangat umum, mungkin berdasarkan pada anotasi atau refleksi, bahwa kompiler tidak dapat berguna secara otomatis menjadi polisi. Bahkan kemudian Anda harus memeriksa untuk melihat apakah ada cara melakukan validasi secara terprogram (misalnya perpustakaan memeriksa sintaks SQL) daripada dengan tangan menggunakan mengejek.

Ini adalah kasus terakhir yang Anda lakukan ketika Anda menguji dengan database lokal 'hidup'; implementasi htable dimulai dan menerapkan validasi kontrak interfacve yang jauh lebih komprehensif daripada yang pernah Anda pikirkan untuk ditulis dengan tangan.

Sayangnya, penggunaan pengujian berbasis mock yang jauh lebih umum adalah pengujian yang:

  • lulus untuk apa pun kode itu pada saat tes ditulis
  • tidak memberikan jaminan tentang sifat apa pun dari kode selain yang ada dan jenis yang dijalankan
  • gagal setiap kali Anda mengubah kode itu

Tentu saja tes semacam itu harus dihapus saat dilihat.

soru
sumber
1
Saya tidak bisa cukup mendukung ini. Saya lebih suka memiliki cakupan 1 pc dengan tes yang bagus dari cakupan pengisi 100pc.
Ian
3
Tes berbasis mock memang menggambarkan kontrak yang digunakan oleh 2 objek untuk berbicara bersama, tetapi mereka jauh melampaui apa yang bisa dilakukan sistem tipe bahasa seperti Java. Ini bukan hanya tentang tanda tangan metode, mereka juga dapat menentukan rentang nilai yang valid untuk argumen atau hasil yang dikembalikan, yang pengecualiannya dapat diterima, dalam urutan apa dan berapa kali metode dapat dipanggil, dll. Kompilator sendiri tidak akan memperingatkan Anda jika ada adalah perubahan pada mereka. Dalam hal itu saya tidak berpikir mereka berlebihan sama sekali. Lihat infoq.com/presentations/integration-tests-scam untuk info lebih lanjut tentang tes berbasis mock.
guillaume31
1
... setuju yaitu menguji logika di sekitar panggilan antarmuka
Rob
1
Tentu saja dapat menambahkan pengecualian yang tidak dicentang, prasyarat yang tidak dideklarasikan, dan status tersirat, ke daftar hal-hal yang membuat antarmuka kurang diketik secara statis, dan dengan demikian membenarkan pengujian berbasis tiruan alih-alih kompilasi sederhana. Masalahnya, bagaimanapun, adalah bahwa ketika aspek-aspek itu berubah, spesifikasinya implisit dan didistribusikan di antara pengujian semua klien. Yang sepertinya tidak akan diperbarui, dan duduklah di sana diam-diam menyembunyikan bug di balik tanda centang hijau.
soru
"spesifikasinya implisit": tidak jika Anda menulis tes kontrak untuk antarmuka Anda ( blog.thecodewhisperer.com/2011/07/07/contract-tests-an-example ) dan patuhi mereka saat membuat tiruan.
guillaume31
5

Berapa lama tes berbasis endpoint dapat dijalankan daripada tes berbasis mock? Jika ini jauh lebih lama, maka ya, layak investasi waktu penulisan tes Anda untuk membuat tes unit lebih cepat - karena Anda harus menjalankannya berkali-kali. Jika tidak signifikan lebih lama, meskipun tes berbasis titik akhir bukan tes unit "murni", selama mereka melakukan pekerjaan yang baik untuk menguji unit, tidak ada alasan untuk bersikap religius tentang hal itu.

Carl Manaster
sumber
4

Saya setuju sepenuhnya dengan respons guillaume31, tidak pernah mengejek tipe yang tidak Anda miliki !.

Biasanya rasa sakit dalam pengujian (mengejek antarmuka yang kompleks) mencerminkan masalah dalam desain Anda. Mungkin Anda memerlukan beberapa abstraksi antara model Anda dan kode akses data Anda, contoh formulir menggunakan arsitektur heksagonal dan pola repositori, cara paling umum untuk menyelesaikan masalah semacam ini.

Jika Anda ingin melakukan tes integrasi untuk memeriksa hal-hal melakukan tes integrasi, jika Anda ingin melakukan tes unit karena Anda menguji logika Anda melakukan tes unit dan mengisolasi dari kegigihan. Tetapi melakukan tes integrasi karena Anda tidak tahu bagaimana mengisolasi logika Anda dari sistem eksternal (atau karena mengisolasi rasa sakitnya) itu bau yang besar, Anda memilih integrasi daripada unit untuk batasan dalam desain Anda bukan untuk kebutuhan nyata. untuk menguji integrasi.

Lihatlah formulir pembicaraan ini Ian cooper: http://vimeo.com/68375232 , ia berbicara tentang arsitektur dan pengujian heksagonal, ia berbicara tentang kapan dan apa yang harus diejek, pembicaraan yang benar-benar terinspirasi yang memecahkan banyak pertanyaan seperti pertanyaan Anda tentang TDD nyata .

AlfredoCasado
sumber
1

TL; DR - Cara saya melihatnya, itu tergantung pada seberapa banyak usaha yang Anda habiskan untuk tes, dan apakah lebih baik menghabiskan lebih banyak pada sistem Anda yang sebenarnya.

Versi panjang:

Beberapa jawaban bagus di sini, tetapi pendapat saya berbeda: pengujian adalah kegiatan ekonomi yang perlu membayar kembali untuk dirinya sendiri, dan jika waktu yang Anda habiskan tidak dikembalikan dalam pengembangan dan keandalan sistem (atau apa pun yang Anda cari untuk keluar tes) maka Anda mungkin melakukan investasi yang buruk; Anda berada dalam bisnis membangun sistem, bukan menulis tes. Oleh karena itu, mengurangi upaya untuk menulis dan mempertahankan tes sangat penting.

Misalnya, beberapa nilai utama yang saya peroleh dari tes adalah:

  • Keandalan (dan karenanya kecepatan pengembangan): kode refactor / mengintegrasikan kerangka kerja baru / menukar komponen / port ke platform yang berbeda, yakinlah bahwa hal-hal masih berfungsi
  • Umpan balik desain: umpan balik TDD / BDD klasik "gunakan kode Anda" pada antarmuka tingkat rendah / menengah Anda

Pengujian terhadap titik akhir langsung masih harus menyediakan ini.

Beberapa kelemahan untuk pengujian terhadap titik akhir langsung:

  • Pengaturan lingkungan - mengonfigurasi dan menstandarisasi lingkungan yang menjalankan tes lebih banyak pekerjaan, dan pengaturan lingkungan yang sedikit berbeda dapat menghasilkan perilaku yang sedikit berbeda.
  • Statelessness - bekerja melawan titik akhir langsung dapat berakhir dengan mempromosikan tes menulis yang mengandalkan keadaan titik akhir bermutasi, yang rapuh dan sulit untuk ditentang (yaitu ketika sesuatu gagal, apakah itu gagal karena keadaan aneh?)
  • Lingkungan pengujian berjalan rapuh - jika tes gagal, apakah tes, kode, atau titik akhir langsung?
  • Kecepatan lari - titik akhir langsung biasanya lebih lambat, dan terkadang lebih sulit untuk diparalelkan
  • Membuat kasus tepi untuk pengujian - biasanya sepele dengan tiruan, kadang-kadang rasa sakit dengan titik akhir langsung (misalnya yang rumit untuk diatur adalah kesalahan transport / HTTP)

Jika saya berada dalam situasi ini, dan kekurangannya tampaknya tidak menjadi masalah sedangkan mengejek titik akhir memperlambat penulisan tes saya, saya akan menguji terhadap titik akhir langsung dalam detak jantung, selama saya akan yakin untuk periksa lagi setelah beberapa saat untuk melihat bahwa kekurangannya ternyata tidak menjadi masalah dalam praktik.

orip
sumber
1

Dari perspektif pengujian ada beberapa persyaratan yang mutlak harus:

  • Pengujian (unit atau lainnya) tidak boleh memiliki cara untuk menyentuh data produksi
  • Hasil dari satu tes tidak boleh mempengaruhi hasil tes lainnya
  • Anda harus selalu memulai dari posisi yang diketahui

Itu tantangan besar ketika menghubungkan ke sumber mana pun yang mempertahankan keadaan di luar tes Anda. Ini bukan TDD "murni", tetapi kru Ruby on Rails menyelesaikan masalah ini dengan cara yang mungkin dapat diadaptasi untuk tujuan Anda. Kerangka uji rel bekerja dengan cara ini:

  • Konfigurasi tes dipilih secara otomatis saat menjalankan tes unit
  • Basis data dibuat dan diinisialisasi pada awal menjalankan unit test
  • Basis data dihapus setelah unit test dijalankan
  • Jika menggunakan SqlLite, konfigurasi pengujian menggunakan basis data RAM

Semua pekerjaan ini dibangun ke dalam test harness, dan bekerja dengan cukup baik. Ada banyak lagi, tetapi dasar-dasarnya sudah cukup untuk percakapan ini.

Pada tim berbeda yang telah bekerja sama dengan saya dari waktu ke waktu, kami akan membuat pilihan yang akan mempromosikan kode diuji meskipun itu bukan jalan yang paling benar. Idealnya, kami akan membungkus semua panggilan ke penyimpanan data dengan kode yang kami kontrol. Secara teori, jika salah satu dari proyek-proyek lama ini mendapatkan dana baru, kita dapat kembali dan memindahkannya dari basis data yang terikat ke Hadoop dengan memfokuskan perhatian kita hanya pada beberapa kelas saja.

Aspek penting adalah tidak mengacaukan data produksi, dan pastikan Anda benar-benar menguji apa yang Anda pikir Anda uji. Sangat penting untuk dapat mereset layanan eksternal ke baseline yang diketahui berdasarkan permintaan - bahkan dari kode Anda.

Berin Loritsch
sumber