Jadi hari ini saya berbicara dengan rekan tim saya tentang pengujian unit. Semuanya dimulai ketika dia bertanya kepada saya "hei, di mana tes untuk kelas itu, saya hanya melihat satu?". Seluruh kelas adalah manajer (atau layanan jika Anda lebih suka menyebutnya seperti itu) dan hampir semua metode hanya mendelegasikan barang ke DAO sehingga mirip dengan:
SomeClass getSomething(parameters) {
return myDao.findSomethingBySomething(parameters);
}
Semacam boilerplate tanpa logika (atau setidaknya saya tidak menganggap delegasi sederhana seperti logika) tetapi boilerplate yang berguna dalam banyak kasus (pemisahan lapisan dll.). Dan kami melakukan diskusi yang cukup panjang apakah saya harus mengujinya atau tidak (saya pikir perlu disebutkan bahwa saya melakukan pengujian unit sepenuhnya terhadap DAO). Argumen utamanya adalah bahwa itu bukan TDD (jelas) dan bahwa seseorang mungkin ingin melihat tes untuk memeriksa apa metode ini (saya tidak tahu bagaimana itu bisa lebih jelas) atau bahwa di masa depan seseorang mungkin ingin mengubah implementasi dan tambahkan logika baru (atau lebih seperti "apa pun") ke dalamnya (dalam hal ini saya kira seseorang harus menguji logika itu ).
Ini membuat saya berpikir. Haruskah kita berusaha untuk% cakupan tes tertinggi? Atau apakah itu hanya seni demi seni? Saya sama sekali tidak melihat alasan di balik pengujian hal-hal seperti:
- getter dan setter (kecuali mereka benar-benar memiliki beberapa logika di dalamnya)
- kode "boilerplate"
Jelas tes untuk metode seperti itu (dengan mengolok-olok) akan memakan waktu kurang dari satu menit tapi saya kira itu masih membuang waktu dan satu milidetik lebih lama untuk setiap CI.
Apakah ada alasan rasional / tidak "mudah terbakar" mengapa seseorang harus menguji setiap baris kode (atau sebanyak mungkin)?
sumber
Jawaban:
Saya mengikuti aturan praktis Kent Beck:
Tes semua yang mungkin bisa pecah.
Tentu saja, itu subjektif sampai batas tertentu. Bagi saya, getter / setter sepele dan one-liner seperti milik Anda di atas biasanya tidak sepadan. Tapi sekali lagi, saya menghabiskan sebagian besar waktu saya menulis tes unit untuk kode warisan, hanya bermimpi tentang proyek TDD greenfield yang bagus ... Pada proyek seperti itu, aturannya berbeda. Dengan kode lawas, tujuan utamanya adalah untuk mencakup sebanyak mungkin tanah dengan upaya sesedikit mungkin, sehingga unit test cenderung lebih tinggi dan lebih kompleks, lebih mirip tes integrasi jika seseorang jago tentang terminologi. Dan ketika Anda berjuang untuk mendapatkan cakupan kode keseluruhan naik dari 0%, atau hanya berhasil meningkatkannya lebih dari 25%, unit test getter dan setter adalah yang paling dikhawatirkan.
OTOH dalam proyek TDD greenfield, mungkin lebih penting untuk menulis tes bahkan untuk metode tersebut. Terutama karena Anda telah menulis tes sebelum Anda mendapatkan kesempatan untuk mulai bertanya-tanya "apakah satu baris ini layak untuk tes khusus?". Dan setidaknya tes-tes ini sepele untuk ditulis dan cepat dijalankan, jadi itu bukan masalah besar.
sumber
Ada beberapa jenis pengujian unit:
Jika Anda menulis tes Anda terlebih dahulu, itu akan lebih masuk akal - seperti yang Anda harapkan untuk memanggil lapisan akses data. Tes akan gagal pada awalnya. Anda kemudian akan menulis kode produksi untuk lulus ujian.
Idealnya Anda harus menguji kode logis, tetapi interaksi (objek yang memanggil objek lain) sama pentingnya. Dalam kasus Anda, saya akan melakukannya
Saat ini tidak ada logika di sana, tetapi tidak akan selalu demikian.
Namun, jika Anda yakin bahwa tidak akan ada logika dalam metode ini dan kemungkinan akan tetap sama, daripada saya akan mempertimbangkan memanggil lapisan akses data langsung dari konsumen. Saya akan melakukan ini hanya jika anggota tim lainnya ada di halaman yang sama. Anda tidak ingin mengirim pesan yang salah ke tim dengan mengatakan "Hai teman-teman, boleh saja mengabaikan lapisan domain, panggil saja lapisan akses data secara langsung".
Saya juga akan berkonsentrasi pada pengujian komponen lain jika ada tes integrasi untuk metode ini. Saya belum melihat perusahaan dengan tes integrasi yang solid.
Setelah mengatakan semua ini - saya tidak akan menguji semuanya secara membabi buta. Saya akan membangun hot spot (komponen dengan kompleksitas tinggi dan risiko pecah tinggi). Saya kemudian akan berkonsentrasi pada komponen-komponen ini. Tidak ada gunanya memiliki basis kode di mana 90% basis kode cukup mudah dan dicakup oleh unit test, ketika sisanya 10% merupakan logika inti dari sistem dan mereka tidak tercakup oleh unit test karena kompleksitasnya.
Akhirnya, apa manfaat dari pengujian metode ini? Apa implikasinya jika ini tidak berhasil? Apakah itu bencana? Jangan berusaha untuk mendapatkan cakupan kode yang tinggi. Cakupan kode harus merupakan produk sampingan dari unit test yang baik. Misalnya Anda dapat menulis satu tes yang akan berjalan di pohon dan memberi Anda cakupan 100% dari metode ini, atau Anda dapat menulis tiga tes unit yang juga akan memberi Anda cakupan 100%. Perbedaannya adalah bahwa dengan menulis tiga tes Anda menguji kasus tepi, bukan hanya berjalan pohon.
sumber
Inilah cara yang baik untuk memikirkan kualitas perangkat lunak Anda:
Untuk fungsi boilerplate dan sepele, Anda dapat mengandalkan pemeriksaan tipe untuk melakukan pekerjaannya, dan untuk yang lain, Anda memang perlu uji kasus.
sumber
Menurut pendapat saya, kompleksitas siklomatik adalah parameter. Jika suatu metode tidak cukup kompleks (seperti getter dan setter). Tidak diperlukan pengujian unit. Tingkat Kompleksitas Siklus McCabe harus lebih dari 1. Kata lain harus ada minimum 1 pernyataan blok.
sumber
YA tegas dengan TDD (dan dengan beberapa pengecualian)
Baik kontroversial, tapi saya berpendapat bahwa siapa pun yang menjawab 'tidak' untuk pertanyaan ini kehilangan konsep dasar TDD.
Bagi saya, jawabannya adalah ya jika Anda mengikuti TDD. Jika tidak maka tidak ada jawaban yang masuk akal.
DDD dalam TDD
TDD sering dikutip memiliki manfaat utama bagimu.
Pisahkan tanggung jawab dari implementasi
Sebagai programmer, sangat menggoda untuk menganggap atribut sebagai sesuatu yang penting dan getter dan setter sebagai semacam overhead.
Tetapi atribut adalah detail implementasi, sementara setter dan getter adalah antarmuka kontrak yang benar-benar membuat program bekerja.
Jauh lebih penting untuk mengeja bahwa objek harus:
dan
lalu bagaimana keadaan ini sebenarnya disimpan (yang atributnya paling umum, tetapi bukan satu-satunya cara).
Tes seperti
Penting untuk bagian dokumentasi TDD.
Fakta bahwa implementasi akhirnya adalah sepele (atribut) dan tidak membawa manfaat pertahanan seharusnya tidak diketahui oleh Anda ketika Anda menulis tes.
Kurangnya rekayasa bolak-balik ...
Salah satu masalah utama dalam dunia pengembangan sistem adalah tidak adanya round-trip engineering 1 - proses pengembangan suatu sistem terpecah menjadi sub-proses yang terpotong-potong, yang artefak-artefaknya (dokumentasi, kode) sering tidak konsisten.
1 Brodie, Michael L. "John Mylopoulos: menjahit benih pemodelan konseptual." Pemodelan Konseptual: Yayasan dan Aplikasi. Springer Berlin Heidelberg, 2009. 1-9.
... dan bagaimana TDD memecahkannya
Ini adalah bagian dokumentasi TDD yang memastikan bahwa spesifikasi sistem dan kodenya selalu konsisten.
Desain dulu, implementasikan nanti
Dalam TDD kami menulis tes penerimaan gagal pertama, baru kemudian menulis kode yang membiarkan mereka lulus.
Di dalam BDD tingkat yang lebih tinggi, kita menulis skenario terlebih dahulu, lalu membuatnya lewat.
Mengapa Anda harus mengecualikan setter dan pengambil?
Secara teori, sangat mungkin dalam TDD untuk satu orang untuk menulis tes, dan yang lain untuk mengimplementasikan kode yang membuatnya lulus.
Jadi tanyakan pada diri sendiri:
Karena getter dan setter adalah antarmuka publik ke kelas, jawabannya jelas ya , atau tidak akan ada cara untuk mengatur atau menanyakan keadaan suatu objek.
Jelas, jika Anda menulis kode terlebih dahulu, jawabannya mungkin tidak begitu jelas.
Pengecualian
Ada beberapa pengecualian yang jelas untuk aturan ini - fungsi yang jelas detail implementasi dan jelas bukan bagian dari desain sistem.
Misalnya, metode lokal 'B ()':
Atau fungsi pribadi di
square()
sini:Atau fungsi lain yang bukan bagian dari
public
antarmuka yang perlu ejaan dalam desain komponen sistem.sumber
Ketika dihadapkan dengan pertanyaan filosofis, kembalilah ke persyaratan mengemudi.
Apakah tujuan Anda untuk menghasilkan perangkat lunak yang cukup andal dengan biaya kompetitif?
Atau apakah itu untuk menghasilkan perangkat lunak dengan keandalan setinggi mungkin hampir terlepas dari biaya?
Sampai pada suatu titik, dua tujuan kualitas dan pengembangan kecepatan / penyelarasan biaya: Anda menghabiskan lebih sedikit waktu menulis tes daripada memperbaiki cacat.
Tapi di luar itu, mereka tidak. Tidak terlalu sulit untuk mencapai, katakanlah, satu bug yang dilaporkan per pengembang per bulan. Membagi dua menjadi satu per dua bulan hanya mengeluarkan anggaran mungkin satu atau dua hari, dan banyak pengujian tambahan mungkin tidak akan mengurangi separuh tingkat cacat Anda. Jadi itu bukan lagi win-win yang sederhana; Anda harus membenarkannya berdasarkan biaya cacat kepada pelanggan.
Biaya ini akan bervariasi (dan, jika Anda ingin menjadi jahat, demikian juga kemampuan mereka untuk menegakkan biaya itu kembali kepada Anda, baik melalui pasar atau gugatan). Anda tidak ingin menjadi jahat, jadi Anda menghitung semua biaya itu kembali; terkadang beberapa tes masih secara global membuat dunia lebih miskin dengan keberadaan mereka.
Singkatnya, jika Anda mencoba secara membuta menerapkan standar yang sama ke situs web in-house sebagai perangkat lunak penerbangan penumpang pesawat, Anda akan berakhir entah dari bisnis, atau di penjara.
sumber
Jawaban Anda tentang ini tergantung pada filosofi Anda (percaya itu adalah Chicago vs London? Saya yakin seseorang akan mencarinya). Juri masih melakukan ini pada pendekatan yang paling efektif waktu (karena, bagaimanapun juga itu penggerak terbesar dari ini - lebih sedikit waktu yang dihabiskan untuk perbaikan).
Beberapa pendekatan mengatakan hanya menguji antarmuka publik, yang lain mengatakan menguji urutan setiap panggilan fungsi di setiap fungsi. Banyak perang suci telah terjadi. Saran saya adalah mencoba kedua pendekatan. Pilih satu unit kode dan lakukan seperti X, dan yang lain seperti Y. Setelah beberapa bulan pengujian dan integrasi kembali dan lihat mana yang sesuai dengan kebutuhan Anda dengan lebih baik.
sumber
Itu pertanyaan yang sulit.
Sebenarnya saya akan mengatakan bahwa itu tidak perlu. Anda lebih baik menulis unit gaya BDD dan tes tingkat sistem yang memastikan fungsi persyaratan bisnis sebagaimana dimaksud dalam skenario positif dan negatif.
Yang mengatakan jika metode Anda tidak dicakup oleh kasus uji ini maka Anda harus mempertanyakan mengapa itu ada di tempat pertama dan jika diperlukan, atau jika ada persyaratan tersembunyi dalam kode yang tidak tercermin dalam dokumentasi atau cerita pengguna Anda yang harus dikodekan dalam kasus uji gaya BDD.
Secara pribadi saya ingin menjaga cakupan dengan garis di sekitar 85-95% dan gerbang check-in ke arus utama untuk memastikan cakupan tes unit yang ada per baris mencapai tingkat ini untuk semua file kode dan tidak ada file yang dibuka.
Dengan asumsi praktik pengujian terbaik sedang diikuti ini memberikan banyak cakupan tanpa memaksa pengembang membuang waktu mencoba mencari cara untuk mendapatkan cakupan tambahan pada kode latihan atau kode sepele hanya demi cakupan.
sumber
Masalahnya adalah pertanyaan itu sendiri, Anda tidak perlu menguji semua "methdos" atau semua "kelas" yang Anda butuhkan untuk menguji semua fitur sistem Anda.
Pemikiran utamanya adalah segi fitur / perilaku alih-alih berpikir dalam hal metode dan kelas. Tentu saja metode ini ada di sini untuk memberikan dukungan untuk satu atau lebih fitur, pada akhirnya semua kode Anda diuji, setidaknya semua masalah kode dalam basis kode Anda.
Dalam skenario Anda, mungkin kelas "manajer" ini berlebihan atau tidak perlu (seperti semua kelas dengan nama yang berisi kata "manajer"), atau mungkin tidak, tetapi tampaknya seperti detail implementasi, mungkin kelas ini tidak pantas satu unit Tes karena kelas ini tidak memiliki logika bisnis yang relevan. Mungkin Anda memerlukan kelas ini agar beberapa fitur berfungsi, tes untuk fitur ini mencakup kelas ini, dengan cara ini Anda dapat melakukan refactor pada kelas ini dan melakukan tes yang memverifikasi bahwa hal yang penting, fitur Anda, masih berfungsi setelah refactor.
Pikirkan fitur / perilaku tidak dalam kelas metode, saya tidak bisa mengulangi ini cukup kali.
sumber
Ya, idealnya 100%, tetapi beberapa hal tidak dapat diuji unit.
Getters / Setters itu bodoh - hanya saja jangan menggunakannya. Alih-alih, masukkan variabel anggota Anda ke bagian publik.
Dapatkan kode umum keluar, dan unit mengujinya. Itu harus sesederhana itu.
Dengan tidak melakukannya, Anda mungkin kehilangan beberapa bug yang sangat jelas. Tes unit seperti jaring yang aman untuk menangkap bug jenis tertentu, dan Anda harus menggunakannya sebanyak mungkin.
Dan hal terakhir: Saya sedang mengerjakan proyek di mana orang tidak mau membuang waktu untuk menulis tes unit untuk "kode sederhana", tetapi kemudian mereka memutuskan untuk tidak menulis sama sekali. Pada akhirnya, bagian dari kode itu berubah menjadi bola lumpur besar .
sumber