Jika kode uji unit Anda “berbau”, apakah itu penting?

52

Biasanya saya hanya melempar tes unit saya bersama-sama menggunakan copy dan paste dan semua jenis praktik buruk lainnya. Tes unit biasanya berakhir sangat jelek, mereka penuh dengan "bau kode," tetapi apakah ini benar-benar penting? Saya selalu mengatakan pada diri sendiri selama kode "nyata" adalah "baik" hanya itu yang penting. Plus, pengujian unit biasanya membutuhkan berbagai "smelly hacks" seperti fungsi stubbing.

Seberapa prihatin seharusnya saya terhadap tes unit yang dirancang dengan buruk ("bau")?

Buttons840
sumber
8
Seberapa sulit untuk memperbaiki kode yang selalu Anda salin?
JeffO
7
kode bau dalam tes bisa dengan mudah menjadi indikator bau tersembunyi di sisa kode - mengapa mendekati tes seolah-olah mereka bukan kode "nyata"?
HorusKol

Jawaban:

75

Apakah tes unit berbau penting? Iya tentu saja. Namun, mereka berbeda dari bau kode karena tes unit melayani tujuan yang berbeda dan memiliki serangkaian ketegangan yang berbeda yang menginformasikan desain mereka. Banyak bau dalam kode tidak berlaku untuk tes. Mengingat mentalitas TDD saya, saya sebenarnya berpendapat bahwa bau unit test lebih penting daripada bau kode karena kode itu hanya ada untuk memenuhi tes.

Berikut adalah beberapa bau unit pengujian umum:

  • Kerapuhan : apakah pengujian Anda sering gagal dan tidak terduga bahkan untuk perubahan kode yang tampaknya sepele atau tidak terkait?
  • Status Kebocoran : apakah pengujian Anda gagal secara berbeda bergantung pada, misalnya, urutan apa pengujian tersebut dijalankan?
  • Setup / Teardown Bloat : Apakah blok setup / teardown Anda panjang dan tumbuh lebih lama? Apakah mereka melakukan segala macam logika bisnis?
  • Runtime Lambat : Apakah tes Anda membutuhkan waktu lama untuk dijalankan? Apakah ada tes unit individual Anda yang membutuhkan waktu lebih dari sepersepuluh detik untuk dijalankan? (Ya, saya serius, sepersepuluh detik.)
  • Gesekan : Apakah tes yang ada membuat sulit untuk menulis tes baru? Apakah Anda sering bergulat dengan kegagalan tes saat melakukan refactoring?

Pentingnya bau adalah bahwa mereka adalah indikator yang berguna dari desain atau masalah lain yang lebih mendasar, yaitu "di mana ada asap, ada api". Jangan hanya mencari aroma tes, cari penyebab dasarnya juga.

Di sini, di sisi lain, ada beberapa praktik yang baik untuk pengujian unit:

  • Umpan Balik Cepat, Terfokus : Tes Anda harus mengisolasi kegagalan dengan cepat dan memberi Anda informasi yang berguna tentang penyebabnya.
  • Minimalkan Jarak Kode Tes : Harus ada jalur yang jelas dan pendek antara tes dan kode yang mengimplementasikannya. Jarak jauh membuat loop umpan balik panjang yang tidak perlu.
  • Tes Satu Hal Sekaligus : Tes unit seharusnya hanya menguji satu hal. Jika Anda perlu menguji hal lain, tulis tes lain.
  • A Bug Is A Test Anda Lupa Untuk Menulis : Apa yang bisa Anda pelajari dari kegagalan menulis tes yang lebih baik dan lebih lengkap di masa depan?
Rein Henrichs
sumber
2
"Tes unit melayani tujuan yang berbeda dan memiliki serangkaian ketegangan berbeda yang menginformasikan desain mereka". Misalnya, mereka harus DAMP, belum tentu KERING.
Jörg W Mittag
3
@ Jorg setuju, tetapi apakah DAMP benar-benar berarti apa-apa? : D
Rein Henrichs
5
@Rein: Ungkapan Deskriptif dan Bermakna. Juga, tidak sepenuhnya KERING. Lihat codeshelter.wordpress.com/2011/04/07/…
Robert Harvey
+1. Saya juga tidak tahu arti DAMP.
egarcia
2
@ironcode Jika Anda tidak dapat bekerja pada satu sistem secara terpisah tanpa takut merusak integrasinya dengan sistem lain, baunya seperti kopling ketat bagi saya. Inilah tujuan mengidentifikasi bau tes seperti runtime panjang: mereka memberi tahu Anda tentang masalah desain seperti kopling ketat. Jawabannya seharusnya tidak "Oh, maka bau tes itu tidak valid", itu harus "Apa bau ini memberi tahu saya tentang desain saya?" Tes unit yang menentukan antarmuka eksternal sistem Anda harus cukup untuk memberi tahu Anda apakah perubahan Anda memecah integrasi dengan konsumennya.
Rein Henrichs
67

Berhati-hatilah. Anda menulis unit test untuk membuktikan kode Anda bertindak seperti yang Anda harapkan. Mereka memungkinkan Anda untuk refactor dengan cepat dengan percaya diri. Jika tes Anda rapuh, sulit dipahami, atau jika sulit dipertahankan, Anda akan mengabaikan tes yang gagal atau mematikannya ketika basis kode Anda berkembang, meniadakan banyak manfaat dari penulisan tes di tempat pertama.

jayraynet
sumber
17
+1 Saat Anda mulai mengabaikan tes yang gagal, mereka tidak menambah nilai.
19

Saya baru saja selesai membaca The Art of Unit Testing beberapa hari yang lalu. Penulis menganjurkan untuk menaruh perhatian yang besar pada pengujian unit Anda seperti halnya Anda melakukan kode produksi.

Saya mengalami tes yang ditulis dengan buruk, tidak dapat dipulihkan secara langsung. Saya sudah menulis sendiri. Ini hampir dijamin bahwa jika tes adalah rasa sakit di keledai untuk mempertahankan, itu tidak akan dipertahankan. Setelah tes tidak sinkron dengan kode yang diuji, mereka menjadi sarang kebohongan dan penipuan. Inti dari unit test adalah untuk menanamkan kepercayaan bahwa kami belum merusak apa pun (yaitu, mereka menciptakan kepercayaan). Jika tes tidak dapat dipercaya, itu lebih buruk daripada tidak berguna.

Joshua Smith
sumber
4
+1 Anda harus mempertahankan tes, seperti kode produksi
Hamish Smith
Buku lain yang sangat bagus tentang topik ini adalah "pola uji xUnit - Refatcoring kode uji" dari Gerard Meszaros.
adrianboimvaser
7

Selama unit test Anda benar-benar menguji kode Anda dalam kasus "kebanyakan". (Kata paling sengaja karena sulit kadang-kadang untuk menemukan semua hasil yang mungkin). Saya pikir kode "bau" adalah preferensi pribadi Anda. Saya selalu menulis kode sedemikian rupa sehingga saya bisa membacanya dan memahaminya dalam beberapa detik, daripada menggali sampah dan mencoba memahami apa itu. Terutama ketika Anda kembali ke sana setelah banyak waktu.

Intinya - pengujiannya, seharusnya mudah. Anda tidak ingin membingungkan diri Anda dengan kode "bau".

Luke
sumber
5
+1: Jangan meributkan kode tes unit. Beberapa pertanyaan paling bodoh berkisar membuat kode tes unit lebih "kuat" sehingga perubahan pemrograman tidak merusak tes unit. Kebodohan. Tes unit dirancang agar rapuh. Tidak apa-apa jika mereka kurang kuat daripada kode aplikasi yang dirancang untuk diuji.
S.Lott
1
@ S.Lott Keduanya setuju dan tidak setuju, untuk alasan yang lebih baik dijelaskan dalam jawaban saya.
Rein Henrichs
1
@Rein Henrichs: itu jauh, bau yang jauh lebih serius daripada yang dijelaskan dalam pertanyaan. "salin dan tempel" dan "terlihat sangat jelek" tidak terdengar seburuk aroma yang Anda gambarkan di mana tes tidak dapat diandalkan.
S.Lott
1
@ S.Lott persis, saya pikir jika kita akan berbicara tentang "pengujian unit bau" yang penting untuk membuat perbedaan itu. Bau kode dalam unit test sering kali tidak. Mereka ditulis untuk tujuan yang berbeda dan ketegangan yang membuat mereka bau dalam satu konteks sangat berbeda dalam konteks yang lain.
Rein Henrichs
2
@ S.Lott btw, ini sepertinya kesempatan sempurna untuk menggunakan fitur obrolan yang banyak terabaikan :)
Rein Henrichs
6

Pastinya. Beberapa orang mengatakan "tes apa pun lebih baik daripada tidak sama sekali". Saya sangat tidak setuju - tes yang ditulis dengan buruk menghambat waktu pengembangan Anda, dan Anda akhirnya menghabiskan waktu berhari-hari untuk memperbaiki tes yang "rusak" karena mereka bukanlah tes unit yang baik. Bagi saya saat ini, dua hal yang saya fokuskan untuk membuat tes saya berharga, daripada beban, adalah:

Maintabilitas

Anda harus menguji hasilnya ( apa yang terjadi), bukan metodenya ( bagaimana itu terjadi). Pengaturan Anda untuk pengujian harus dipisahkan dari implementasi mungkin: hanya mengatur hasil untuk panggilan layanan dll yang benar-benar diperlukan.

  • Gunakan kerangka mengejek untuk memastikan tes Anda tidak bergantung pada apa pun yang eksternal
  • Pilih bertopik di atas tiruan (jika kerangka Anda membedakannya) sedapat mungkin
  • Tidak ada logika dalam tes! Seandainya, switch, untuk masing-masing, case, try-catches dll. Semuanya tidak boleh dilakukan, karena mereka dapat memasukkan bug ke dalam kode tes itu sendiri

Keterbacaan

Tidak apa-apa untuk memungkinkan pengulangan yang lebih banyak dalam pengujian Anda, yang biasanya tidak Anda izinkan dalam kode produksi Anda, jika itu membuatnya lebih mudah dibaca. Seimbangkan ini dengan hal-hal pemeliharaan di atas. Jelaskan secara eksplisit apa yang dilakukan tes ini!

  • Cobalah untuk mempertahankan gaya "atur, bertindak, tegaskan" untuk tes Anda. Ini memisahkan pengaturan Anda dan harapan skenario, dari tindakan yang dilakukan, dan hasilnya dinyatakan.
  • Pertahankan satu pernyataan logis per tes (jika nama tes Anda memiliki "dan" di dalamnya, Anda mungkin perlu memecahnya menjadi beberapa tes)

Sebagai kesimpulan, Anda harus sangat peduli dengan tes "bau" - mereka dapat berakhir hanya membuang-buang waktu Anda, tidak memberikan nilai.

Anda mengatakan:

Unit testing biasanya membutuhkan berbagai "smelly hacks" seperti fungsi stubbing.

Sepertinya Anda bisa melakukannya dengan membaca beberapa teknik Unit Testing, seperti menggunakan kerangka Mocking, untuk membuat hidup Anda lebih mudah. Saya sangat merekomendasikan The Art of Unit Testing , yang mencakup hal-hal di atas dan banyak lagi. Saya menemukan itu mencerahkan setelah berjuang dengan tes "bau" yang ditulis dengan buruk, tidak terpelihara untuk waktu yang lama. Ini adalah salah satu investasi waktu terbaik yang saya buat tahun ini!

mjhilton
sumber
Jawaban yang sangat bagus. Saya hanya punya satu quibble kecil: jauh lebih penting daripada "satu pernyataan logis per tes" adalah satu tindakan per tes. Intinya adalah tidak peduli berapa banyak langkah yang diperlukan untuk mengatur pengujian, seharusnya hanya ada satu "tindakan kode produksi yang diuji". Jika tindakan tersebut harus memiliki lebih dari satu efek samping, Anda mungkin memiliki beberapa pernyataan.
Disillusioned
5

Dua pertanyaan untuk Anda:

  • Apakah Anda benar-benar positif bahwa Anda menguji apa yang Anda pikir Anda uji?
  • Jika orang lain melihat unit test, apakah mereka dapat mengetahui kode apa yang seharusnya dilakukan ?

Ada beberapa cara untuk menangani tugas yang berulang dalam pengujian unit Anda yang paling umum adalah pengaturan dan kode pembongkaran. Pada dasarnya, Anda memiliki metode pengaturan tes dan metode tes runtuh - semua kerangka kerja unit uji mendukung hal ini.

Tes unit harus kecil dan mudah dipahami. Jika tidak dan tes gagal, bagaimana Anda akan memperbaiki masalah dalam waktu yang cukup singkat. Mudahkan diri Anda selama beberapa bulan di jalan ketika Anda harus kembali ke kode.

Berin Loritsch
sumber
5

Ketika sampah mulai berbau, saatnya untuk mengeluarkannya. Kode pengujian Anda harus sebersih kode produksi Anda. Apakah Anda akan menunjukkannya kepada ibumu?

Bill Leeper
sumber
3

Selain jawaban lain di sini.

Kode kualitas yang buruk dalam unit test tidak terbatas pada unit test unit Anda.

Salah satu peran yang diisi oleh Unit Testing adalah Dokumentasi.

Suite Tes Unit adalah salah satu tempat yang terlihat untuk mencari tahu bagaimana API dimaksudkan untuk digunakan.

Penelepon API Anda tidak mungkin untuk menyalin bagian-bagian dari unit pengujian unit Anda, yang mengarah ke kode suite-pengujian Anda yang buruk yang menginfeksi kode langsung di tempat lain.

Paul Butcher
sumber
3

Saya hampir tidak menyerahkan jawaban saya karena butuh beberapa saat untuk mencari cara untuk bahkan mendekati ini sebagai pertanyaan yang sah.

Alasan pemrogram terlibat dalam "praktik terbaik" bukan karena alasan estetika, atau karena mereka menginginkan kode "sempurna". Itu karena menghemat waktu mereka.

  • Menghemat waktu karena kode yang baik lebih mudah dibaca dan dimengerti.
  • Ini menghemat waktu karena ketika Anda perlu menemukan perbaikan bug lebih mudah dan lebih cepat untuk ditemukan.
  • Ini menghemat waktu karena ketika Anda ingin memperpanjang kode lebih mudah dan lebih cepat untuk melakukannya.

Jadi pertanyaan yang Anda tanyakan (dari sudut pandang saya) adalah haruskah saya menghemat waktu atau menulis kode yang akan menghabiskan waktu saya?

Untuk pertanyaan itu saya hanya bisa mengatakan, seberapa penting waktu Anda?

FYI: stubbing, ejekan, dan patching monyet semua memiliki kegunaan yang sah. Mereka hanya "mencium" ketika penggunaannya tidak tepat.

dietbuddha
sumber
2

Saya mencoba secara khusus untuk TIDAK membuat pengujian unit saya terlalu kuat. Saya telah melihat unit test yang mulai melakukan penanganan kesalahan atas nama menjadi kuat. Yang akhirnya Anda lakukan adalah tes yang menelan bug yang mereka coba tangkap. Unit test juga harus melakukan beberapa hal funky cukup sering untuk membuat semuanya berfungsi. Pikirkan seluruh ide pengakses pribadi menggunakan refleksi ... jika saya melihat banyak dari mereka semua dalam kode produksi, saya akan khawatir 9 kali dari 10. Saya pikir lebih banyak waktu harus dimasukkan ke dalam memikirkan tentang apa yang sedang diuji , daripada kebersihan kode. Lagi pula, tes perlu diubah cukup sering jika Anda melakukan refactoring besar, jadi mengapa tidak hanya meretasnya bersama, memiliki perasaan kepemilikan yang sedikit kurang, dan lebih termotivasi untuk menulis ulang atau mengerjakan ulang ketika saatnya tiba?

Morgan Herlocker
sumber
2

Jika Anda ingin mendapatkan cakupan yang berat dan tingkat rendah, Anda akan menghabiskan lebih banyak waktu atau lebih dalam kode uji seperti dalam kode produk ketika melakukan modifikasi serius.

Tergantung pada kerumitan set-up tes kode mungkin ada yang lebih kompleks. (httpcontext.current vs monster mengerikan mencoba membangun yang salah misalnya)

Kecuali jika produk Anda adalah sesuatu di mana Anda jarang melakukan perubahan besar pada antarmuka yang ada dan input unit-level Anda sangat sederhana untuk diatur. Saya akan berada di LEAST karena khawatir tentang pemahaman tes sebagai produk yang sebenarnya.

Tagihan
sumber
0

Code Smells hanya masalah dalam kode yang membuat perlu diubah atau dipahami di beberapa titik nanti.

Jadi saya pikir Anda harus memperbaiki bau kode ketika Anda harus "menutup" ke unit test yang diberikan, tetapi tidak sebelumnya.

Ian
sumber