Apa yang harus Anda uji dengan unit test?

122

Saya baru saja lulus dari perguruan tinggi, dan memulai universitas di suatu tempat minggu depan. Kami telah melihat unit test, tapi kami agak tidak menggunakannya; dan semua orang membicarakannya, jadi saya pikir mungkin saya harus melakukan beberapa.

Masalahnya adalah, saya tidak tahu harus diuji apa . Haruskah saya menguji kasus umum? Kasing tepi? Bagaimana saya tahu bahwa suatu fungsi cukup tertutup?

Saya selalu memiliki perasaan mengerikan bahwa sementara tes akan membuktikan bahwa suatu fungsi berfungsi untuk kasus tertentu, sama sekali tidak berguna untuk membuktikan bahwa fungsi tersebut berfungsi, titik.

zneak
sumber
Lihatlah Blog Roy Osherove . Ada banyak informasi tentang pengujian unit di sana termasuk video. Dia juga telah menulis buku, "Seni Pengujian Unit" yang sangat bagus.
Piers Myers
9
Saya ingin tahu apa pendapat Anda tentang hal itu setelah hampir 5 tahun? Karena semakin banyak saya merasa bahwa orang-orang seharusnya lebih tahu "apa yang tidak harus diuji" saat ini. Pengembangan yang didorong oleh perilaku telah berkembang dari pertanyaan seperti yang Anda ajukan.
Remigijus Pankevičius

Jawaban:

121

Filosofi pribadi saya sejauh ini adalah:

  1. Uji kasus umum dari semua yang Anda bisa. Ini akan memberi tahu Anda ketika kode itu rusak setelah Anda melakukan beberapa perubahan (yang, menurut pendapat saya, satu-satunya manfaat terbesar dari pengujian unit otomatis).
  2. Uji kasus tepi dari beberapa kode kompleks luar biasa yang menurut Anda mungkin akan memiliki kesalahan.
  3. Setiap kali Anda menemukan bug, tulis test case untuk menutupnya sebelum memperbaikinya
  4. Tambahkan tes kasus tepi ke kode yang kurang penting setiap kali seseorang memiliki waktu untuk membunuh.
Fishtoaster
sumber
1
Terima kasih untuk ini, saya bingung di sini dengan pertanyaan yang sama seperti OP.
Stephen
5
+1, meskipun saya juga akan menguji kasus tepi dari setiap fungsi tipe pustaka / utilitas untuk memastikan Anda memiliki API logis. misalnya apa yang terjadi ketika null dilewatkan? bagaimana dengan input kosong? Ini akan membantu memastikan desain Anda logis dan mendokumentasikan perilaku kasus sudut.
mikera
7
# 3 sepertinya jawaban yang sangat solid karena ini adalah contoh nyata tentang bagaimana sebuah unit test dapat membantu. Jika rusak sekali, itu bisa pecah lagi.
Ryan Griffith
Baru saja mulai, saya merasa saya tidak terlalu kreatif dengan menyusun tes. Jadi saya menggunakannya sebagai # 3 di atas, yang menjamin ketenangan pikiran bahwa bug-bug itu tidak akan pernah lagi terdeteksi.
ankush981
Jawaban Anda ditampilkan dalam artikel media populer ini: hackernoon.com/...
BugHunterUK
67

Di antara sejumlah besar jawaban sehingga sejauh ini belum ada yang menyentuh pada partisi kesetaraan dan analisis nilai batas , pertimbangan vital dalam jawaban untuk pertanyaan yang ada. Semua jawaban lain, walaupun bermanfaat, bersifat kualitatif tetapi dimungkinkan - dan lebih disukai - untuk menjadi kuantitatif di sini. @fishtoaster memberikan beberapa pedoman konkret, hanya mengintip di bawah selimut kuantifikasi uji, tetapi partisi kesetaraan dan analisis nilai batas memungkinkan kita melakukan yang lebih baik.

Dalam partisi kesetaraan , Anda membagi set semua input yang mungkin menjadi kelompok berdasarkan hasil yang diharapkan. Setiap input dari satu grup akan menghasilkan hasil yang setara, sehingga grup tersebut disebut kelas ekivalensi . (Perhatikan bahwa hasil yang setara tidak berarti hasil yang identik.)

Sebagai contoh sederhana, pertimbangkan program yang harus mengubah karakter ASCII huruf kecil menjadi karakter huruf besar. Karakter lain harus menjalani transformasi identitas, yaitu tetap tidak berubah. Berikut adalah salah satu kemungkinan pengelompokan ke dalam kelas kesetaraan:

| # |  Equivalence class    | Input        | Output       | # test cases |
+------------------------------------------------------------------------+
| 1 | Lowercase letter      | a - z        | A - Z        | 26           |
| 2 | Uppercase letter      | A - Z        | A - Z        | 26           |
| 3 | Non-alphabetic chars  | 0-9!@#,/"... | 0-9!@#,/"... | 42           |
| 4 | Non-printable chars   | ^C,^S,TAB... | ^C,^S,TAB... | 34           |

Kolom terakhir melaporkan jumlah kasus uji jika Anda menyebutkan semuanya. Secara teknis, dengan aturan @ fishtoaster 1 Anda akan menyertakan 52 kasus uji - semua untuk dua baris pertama yang diberikan di atas termasuk dalam "kasus umum". Aturan @ fishtoaster 2 akan menambahkan beberapa atau semua dari baris 3 dan 4 di atas juga. Tapi dengan kesetaraan partisi menguji setiap satu test case di masing-masing kelas kesetaraan cukup. Jika Anda memilih "a" atau "g" atau "w" Anda sedang menguji jalur kode yang sama. Dengan demikian, Anda memiliki total 4 kasus uji, bukannya 52+.

Analisis nilai batas merekomendasikan sedikit perbaikan: pada dasarnya itu menunjukkan bahwa tidak setiap anggota kelas ekuivalensi, yah, setara. Artinya, nilai pada batas juga harus dianggap layak untuk kasus uji dalam hak mereka sendiri. (Satu pembenaran mudah untuk ini adalah kesalahan off-by-one yang terkenal !) Jadi, untuk setiap kelas ekivalensi Anda dapat memiliki 3 input uji. Melihat domain input di atas - dan dengan sedikit pengetahuan tentang nilai-nilai ASCII - saya mungkin akan menghasilkan input-input kasus uji ini:

| # | Input                | # test cases |
| 1 | a, w, z              | 3            |
| 2 | A, E, Z              | 3            |
| 3 | 0, 5, 9, !, @, *, ~  | 7            |
| 4 | nul, esc, space, del | 4            |

(Segera setelah Anda mendapatkan lebih dari 3 nilai batas yang menunjukkan Anda mungkin ingin memikirkan kembali delineasi kelas ekuivalensi asli Anda, tapi ini cukup sederhana sehingga saya tidak kembali untuk merevisinya.) Dengan demikian, analisis nilai batas membawa kita ke hanya 17 kasus uji - dengan tingkat kepercayaan penuh akan cakupan penuh - dibandingkan dengan 128 kasus uji untuk melakukan pengujian lengkap. (Belum lagi bahwa kombinatorik mendikte bahwa pengujian lengkap hanya tidak layak untuk aplikasi dunia nyata!)

Michael Sorens
sumber
3
+1 Itulah tepatnya bagaimana saya secara intuitif menulis tes saya. Sekarang saya dapat memberi nama di atasnya :) Terima kasih telah berbagi itu.
guillaume31
+1 untuk "jawaban kualitatif berguna, tetapi mungkin - dan lebih disukai - untuk menjadi kuantitatif"
Jimmy Breck-McKye
Saya pikir ini adalah jawaban yang baik jika arahannya adalah "bagaimana saya bisa mendapatkan cakupan yang baik dengan tes saya". Saya pikir akan berguna untuk menemukan pendekatan pragmatis di atas ini - apakah tujuan bahwa setiap cabang dari setiap bagian logika di setiap lapisan harus diuji secara menyeluruh dengan cara ini?
Kieren Johnstone
18

Mungkin pendapat saya tidak terlalu populer. Tetapi saya menyarankan Anda untuk berhemat dengan unit test. Jika Anda memiliki terlalu banyak unit test, Anda dapat dengan mudah menghabiskan setengah dari waktu Anda atau lebih dengan mempertahankan tes daripada coding yang sebenarnya.

Saya menyarankan Anda untuk menulis tes untuk hal-hal yang Anda punya firasat buruk di usus Anda atau hal-hal yang sangat penting dan / atau dasar. Tes unit IMHO bukan pengganti untuk rekayasa yang baik dan pengkodean defensif. Saat ini saya mengerjakan proyek yang kurang lebih tidak dapat digunakan. Ini benar-benar stabil tetapi sakit untuk refactor. Sebenarnya tidak ada yang menyentuh kode ini dalam satu tahun dan tumpukan perangkat lunak yang didasarkan pada adalah 4 tahun. Mengapa? Karena itu berantakan dengan tes unit, tepatnya: Tes unit dan tes integrasi terotomatisasi. (Pernah mendengar mentimun dan sejenisnya?) Dan inilah bagian terbaiknya: Perangkat lunak (yang) tidak dapat digunakan ini telah dikembangkan oleh sebuah perusahaan yang karyawannya adalah pelopor dalam kancah pengembangan yang didorong oleh uji coba. : D

Jadi saran saya adalah:

  • Mulailah menulis tes setelah Anda mengembangkan kerangka dasar, jika tidak, refactoring bisa menyakitkan. Sebagai pengembang yang mengembangkan untuk orang lain, Anda tidak pernah mendapatkan persyaratan sejak awal.

  • Pastikan unit test Anda dapat dilakukan dengan cepat. Jika Anda memiliki tes integrasi (seperti mentimun) tidak apa-apa jika perlu waktu lebih lama. Tapi tes berjalan lama tidak menyenangkan, percayalah. (Orang-orang lupa semua alasan mengapa C ++ menjadi kurang populer ...)

  • Serahkan hal-hal TDD ini kepada para ahli TDD.

  • Dan ya, kadang-kadang Anda berkonsentrasi pada kasus tepi, kadang-kadang pada kasus umum, tergantung di mana Anda mengharapkan yang tidak terduga. Meskipun jika Anda selalu mengharapkan yang tidak terduga, Anda harus benar-benar memikirkan kembali alur kerja dan disiplin Anda. ;-)

Philip
sumber
2
Bisakah Anda memberikan lebih banyak detail mengapa tes ini membuat software ini sulit untuk diperbaiki?
Mike Partridge
6
+1 Besar. Memiliki dinding unit test yang menguji implementasi alih-alih aturan membuat perubahan memerlukan 2-3x lebih banyak
TheLQ
9
Seperti kode produksi yang ditulis dengan buruk, unit test yang ditulis dengan buruk sulit untuk dipertahankan. "Terlalu banyak unit test" sepertinya gagal untuk tetap KERING; setiap tes harus menangani / membuktikan bagian tertentu dari sistem.
Allan
1
Setiap unit test harus memeriksa satu hal, jadi tidak terlalu banyak unit test tetapi, tes yang hilang. Jika tes unit Anda rumit, itu masalah lain.
graffic
1
-1: Saya pikir posting ini ditulis dengan buruk. Ada banyak hal yang disebutkan, dan saya tidak tahu bagaimana mereka semua berhubungan. Jika inti jawabannya adalah "ekonomis", lalu bagaimana hubungan contoh Anda? Sepertinya situasi contoh Anda (meskipun nyata) memiliki tes unit yang buruk. Tolong jelaskan pelajaran apa yang harus saya pelajari dari itu dan bagaimana hal itu membantu saya menjadi ekonomis. Juga, jujur ​​saya, tidak tahu apa yang Anda maksud ketika Anda mengatakannya Leave this TDD stuff to the TDD-experts.
Alexander Bird
8

Jika Anda menguji terlebih dahulu dengan Test Driven Development, maka cakupan Anda akan naik di kisaran 90% atau lebih tinggi, karena Anda tidak akan menambahkan fungsionalitas tanpa terlebih dahulu menulis unit test yang gagal untuknya.

Jika Anda menambahkan tes setelah fakta, maka saya tidak bisa merekomendasikan cukup bahwa Anda mendapatkan salinan Bekerja Efektif dengan Kode Legacy oleh Michael Feathers dan lihat beberapa teknik untuk menambahkan tes ke kode Anda dan cara refactoring kode Anda untuk membuatnya lebih dapat diuji.

Paddyslacker
sumber
Bagaimana Anda menghitung persentase cakupan itu? Apa artinya mencakup 90% kode Anda?
zneak
2
@zneak: ada alat cakupan kode yang akan menghitungnya untuk Anda. Google cepat untuk "cakupan kode" akan memunculkan beberapa dari mereka. Alat ini melacak baris kode yang dieksekusi saat menjalankan tes, dan mendasarkan yang sesuai dengan garis total kode dalam perakitan untuk menghasilkan persentase cakupan.
Steven Evers
-1. Tidak menjawab pertanyaan:The problem is, I don't know _what_ to test
Alexander Bird
6

Jika Anda mulai mengikuti praktik Pengembangan yang Didorong oleh Tes , mereka akan menyortir memandu Anda melalui proses dan mengetahui apa yang akan diuji akan muncul secara alami. Beberapa tempat untuk memulai:

Tes didahulukan

Jangan pernah menulis kode sebelum menulis tes. Lihat Red-Green-Refactor-Ulangi untuk penjelasan.

Tulis tes regresi

Setiap kali Anda menemukan bug, tulis testcase, dan pastikan gagal . Kecuali Anda dapat mereproduksi bug melalui testcase yang gagal, Anda belum benar-benar menemukannya.

Merah-Hijau-Refactor-Ulangi

Merah : Mulailah dengan menulis tes paling dasar untuk perilaku yang Anda coba terapkan. Pikirkan langkah ini saat menulis beberapa kode contoh yang menggunakan kelas atau fungsi yang sedang Anda kerjakan. Pastikan ia mengkompilasi / tidak memiliki kesalahan sintaks dan gagal . Ini harus jelas: Anda belum menulis kode apa pun, jadi pasti gagal, bukan? Yang penting untuk dipelajari di sini adalah bahwa kecuali Anda melihat tes gagal setidaknya satu kali, Anda tidak akan pernah yakin bahwa jika lulus, ia melakukannya karena sesuatu yang telah Anda lakukan karena beberapa alasan palsu.

Hijau : Tulis kode paling sederhana dan bodoh yang benar-benar membuat lulus ujian. Jangan mencoba menjadi pintar. Bahkan jika Anda melihat bahwa ada kasus tepi yang jelas tetapi tes memperhitungkan, jangan menulis kode untuk menanganinya (tapi jangan lupa tentang kasus tepi: Anda akan membutuhkannya nanti). Idenya adalah bahwa setiap bagian dari kode yang Anda tulis, setiap if, setiap try: ... except: ...harus dibenarkan oleh sebuah test case. Kode tidak harus elegan, cepat, atau dioptimalkan. Anda hanya ingin lulus ujian.

Refactor : Bersihkan kode Anda, dapatkan nama metode yang benar. Lihat apakah tes masih berlalu. Optimalkan. Jalankan tes lagi.

Ulangi : Anda ingat kasing tepi yang tes tidak menutupi, kan? Jadi, sekarang ini momen besar. Tulis testcase yang mencakup situasi itu, saksikan gagal, tulis beberapa kode, lihat lulus, refactor.

Uji kode Anda

Anda sedang mengerjakan beberapa kode tertentu, dan inilah yang ingin Anda uji. Ini berarti bahwa Anda tidak boleh menguji fungsi perpustakaan, perpustakaan standar atau kompiler Anda. Juga, cobalah untuk menghindari menguji "dunia". Ini termasuk: memanggil API web eksternal, beberapa hal intensif basis data, dll. Setiap kali Anda dapat mencoba mengejanya (buat objek yang mengikuti antarmuka yang sama, tetapi mengembalikan data statis yang telah ditentukan).

Ryszard Szopa
sumber
1
Dengan asumsi saya sudah memiliki basis kode kerja yang sudah ada dan (sejauh yang saya bisa lihat), apa yang harus saya lakukan?
zneak
Itu mungkin sedikit lebih sulit (tergantung bagaimana kode ditulis). Mulailah dengan tes regresi (selalu masuk akal), kemudian Anda dapat mencoba menulis tes unit untuk membuktikan kepada diri sendiri bahwa Anda memahami apa yang dilakukan kode. Sangat mudah untuk kewalahan dengan jumlah pekerjaan yang (tampaknya) harus dilakukan, tetapi: beberapa tes selalu lebih baik daripada tidak ada tes sama sekali.
Ryszard Szopa
3
-1 Saya tidak berpikir itu jawaban yang sangat baik untuk pertanyaan ini . Pertanyaannya bukan tentang TDD, tetapi bertanya tentang apa yang harus diuji ketika menulis tes unit. Saya pikir jawaban yang baik untuk pertanyaan aktual harus diterapkan pada metodologi non-TDD.
Bryan Oakley
1
Jika Anda menyentuhnya, ujilah. Dan Clean Code (Robert C Martin) menyarankan Anda menulis "tes pembelajaran" untuk kode pihak ketiga. Dengan begitu Anda belajar menggunakannya, dan Anda memiliki tes jika versi baru mengubah perilaku yang Anda gunakan.
Roger Willcocks
3

Untuk pengujian unit, mulailah dengan pengujian yang melakukan apa yang dirancang untuk dilakukan. Itu harus menjadi kasus pertama yang Anda tulis. Jika bagian dari desain adalah "harus mengeluarkan pengecualian jika Anda lulus dalam sampah", uji itu juga karena itu adalah bagian dari desain.

Mulailah dengan itu. Ketika Anda mendapatkan pengalaman dengan melakukan hal yang paling mendasar dari pengujian Anda akan mulai belajar apakah itu cukup atau tidak, dan mulai melihat aspek-aspek lain dari kode Anda yang perlu diuji.

Bryan Oakley
sumber
0

Jawaban utama adalah untuk "menguji segala sesuatu yang mungkin bisa pecah" .

Apa yang terlalu sederhana untuk dihancurkan? Bidang data, pengakses properti mati-otak, dan overhead boilerplate serupa. Hal lain mungkin mengimplementasikan sebagian persyaratan yang dapat diidentifikasi, dan mungkin mendapat manfaat dari pengujian.

Tentu saja, jarak tempuh Anda - dan praktik lingkungan kerja Anda - dapat bervariasi.

Jeffrey Hantin
sumber
Baik. Jadi kasus mana yang harus saya uji? Kasus "normal"? Kasing tepi?
zneak
3
Aturan praktis? Satu atau dua tepat di tengah jalan emas, dan tepat di dalam dan di luar setiap tepi.
Jeffrey Hantin
@ JeffreyHantin Itulah "analisis nilai batas" dalam jawaban yang berbeda.
Roger Willcocks