Saya mencoba membuat kode saya lebih kuat dan saya telah membaca tentang pengujian unit, tetapi saya merasa sangat sulit untuk menemukan penggunaan yang sebenarnya berguna. Misalnya, contoh Wikipedia :
public class TestAdder {
public void testSum() {
Adder adder = new AdderImpl();
assert(adder.add(1, 1) == 2);
assert(adder.add(1, 2) == 3);
assert(adder.add(2, 2) == 4);
assert(adder.add(0, 0) == 0);
assert(adder.add(-1, -2) == -3);
assert(adder.add(-1, 1) == 0);
assert(adder.add(1234, 988) == 2222);
}
}
Saya merasa bahwa tes ini sama sekali tidak berguna, karena Anda diharuskan menghitung secara manual hasil yang diinginkan dan mengujinya, saya merasa seperti unit test yang lebih baik di sini adalah
assert(adder.add(a, b) == (a+b));
tapi kemudian ini hanya mengkode fungsi itu sendiri dalam ujian. Dapatkah seseorang memberi saya contoh di mana pengujian unit sebenarnya berguna? FYI Saya saat ini mengkode sebagian besar fungsi "prosedural" yang mengambil ~ 10 boolean dan beberapa int dan memberi saya hasil int berdasarkan ini, saya merasa seperti satu-satunya pengujian unit yang bisa saya lakukan adalah dengan hanya mengkode ulang algoritma dalam uji. sunting: Saya juga seharusnya telah memprioritaskan ini saat porting (mungkin dirancang dengan buruk) kode ruby (yang saya tidak buat)
sumber
How does unit testing work?
Tidak ada satu yang benar-benar tahu :)Jawaban:
Tes unit, jika Anda menguji unit yang cukup kecil, selalu menyatakan yang sangat jelas.
Alasan bahwa
add(x, y)
bahkan mendapat menyebutkan unit test, adalah karena suatu saat nanti seseorang akan masuk keadd
dan memasukkan kode penanganan logika pajak khusus tidak menyadari bahwa add digunakan di mana-mana.Tes unit sangat banyak tentang prinsip asosiatif: jika A melakukan B, dan B melakukan C, maka A melakukan C. "A melakukan C" adalah tes tingkat yang lebih tinggi. Misalnya, pertimbangkan kode bisnis berikut yang sepenuhnya sah:
Sepintas ini terlihat seperti metode yang luar biasa untuk pengujian unit, karena memiliki tujuan yang sangat jelas. Namun, ia melakukan sekitar 5 hal berbeda. Setiap hal memiliki kasus yang valid dan tidak valid, dan akan membuat permutasi yang sangat besar dari unit test. Idealnya ini dirinci lebih lanjut:
Sekarang kita ke unit. Satu unit tes tidak peduli untuk apa
_userRepo
perilaku yang dianggap validFetchValidUser
, hanya itu yang disebut. Anda dapat menggunakan tes lain untuk memastikan apa yang dimaksud dengan pengguna yang valid. Demikian pula untukCheckUserForRole
... Anda telah memisahkan tes Anda dari mengetahui seperti apa struktur Peran itu. Anda juga telah memisahkan seluruh program agar tidak diikat dengan ketatSession
. Saya membayangkan semua bagian yang hilang di sini akan terlihat seperti:Dengan refactoring Anda telah menyelesaikan beberapa hal sekaligus. Program ini jauh lebih mendukung untuk merobohkan struktur yang mendasarinya (Anda dapat membuang lapisan basis data untuk NoSQL), atau menambahkan penguncian dengan mulus begitu Anda sadar bahwa
Session
tidak ada thread-safe atau apa pun. Anda juga sekarang telah memberikan diri Anda tes yang sangat sederhana untuk menulis untuk tiga dependensi ini.Semoga ini membantu :)
sumber
Saya cukup yakin bahwa masing-masing fungsi prosedural Anda bersifat deterministik, sehingga mengembalikan hasil khusus untuk setiap rangkaian nilai input yang diberikan. Idealnya, Anda akan memiliki spesifikasi fungsional dari mana Anda dapat mengetahui hasil apa yang harus Anda terima untuk set nilai input tertentu. Dalam kekurangan itu, Anda dapat menjalankan kode ruby (yang dianggap berfungsi dengan benar) untuk set nilai input tertentu, dan merekam hasilnya. Kemudian, Anda perlu KERAS KODE hasil tes Anda. Tes ini seharusnya menjadi bukti bahwa kode Anda memang menghasilkan hasil yang diketahui benar .
sumber
Karena tampaknya tidak ada orang lain yang memberikan contoh nyata:
Kamu bilang:
Tetapi intinya adalah bahwa Anda jauh lebih kecil kemungkinannya untuk membuat kesalahan (atau paling tidak lebih mungkin untuk melihat kesalahan Anda) ketika melakukan perhitungan manual kemudian ketika coding.
Seberapa besar kemungkinan Anda membuat kesalahan dalam kode konversi desimal ke roman Anda? Sangat mungkin. Seberapa besar kemungkinan Anda membuat kesalahan ketika mengonversi angka desimal ke angka romawi dengan tangan? Sangat tidak mungkin. Itu sebabnya kami menguji terhadap perhitungan manual.
Seberapa besar kemungkinan Anda membuat kesalahan ketika menerapkan fungsi root kuadrat? Sangat mungkin. Seberapa besar kemungkinan Anda melakukan kesalahan saat menghitung akar kuadrat dengan tangan? Mungkin lebih mungkin. Tetapi dengan sqrt, Anda dapat menggunakan kalkulator untuk mendapatkan jawabannya.
Jadi saya akan berspekulasi tentang apa yang terjadi di sini. Fungsi Anda agak rumit, jadi sulit untuk mengetahui dari input apa output seharusnya. Untuk melakukan itu, Anda harus secara manual menjalankan (di kepala Anda) fungsi untuk mencari tahu apa outputnya. Maklum, itu sepertinya tidak berguna dan rawan kesalahan.
Kuncinya adalah Anda ingin menemukan output yang benar. Tetapi Anda harus menguji output tersebut terhadap sesuatu yang diketahui benar. Ini tidak baik untuk menulis algoritma Anda sendiri untuk menghitung itu karena itu mungkin sangat tidak benar. Dalam hal ini terlalu sulit untuk menghitung nilai secara manual.
Saya akan kembali ke kode ruby, dan menjalankan fungsi asli ini dengan berbagai parameter. Saya akan mengambil hasil dari kode ruby dan memasukkannya ke dalam unit test. Dengan begitu Anda tidak perlu melakukan perhitungan manual. Tetapi Anda menguji terhadap kode asli. Itu seharusnya membantu menjaga hasil yang sama, tetapi jika ada bug di aslinya maka itu tidak akan membantu Anda. Pada dasarnya, Anda dapat memperlakukan kode asli seperti kalkulator pada contoh sqrt.
Jika Anda menunjukkan kode aktual yang Anda porting, kami dapat memberikan umpan balik yang lebih rinci tentang cara mendekati masalah.
sumber
Anda hampir benar untuk kelas yang begitu sederhana.
Cobalah untuk kalkulator yang lebih kompleks .. Seperti kalkulator skor bowling.
Nilai unit test lebih mudah dilihat ketika Anda memiliki aturan "bisnis" yang lebih kompleks dengan skenario berbeda untuk diuji.
Saya tidak mengatakan Anda tidak boleh menguji coba kalkulator pabrik (Apakah akun kalkulator Anda mengeluarkan nilai seperti 1/3 yang tidak dapat diwakili? Apa hubungannya dengan pembagian dengan nol?) Tetapi Anda akan melihat nilai lebih jelas jika Anda menguji sesuatu dengan lebih banyak cabang untuk mendapatkan cakupan.
sumber
Meskipun fanatik agama sekitar 100% cakupan kode, saya akan mengatakan bahwa tidak setiap metode harus diuji unit. Hanya fungsionalitas yang berisi logika bisnis yang bermakna. Fungsi yang hanya menambahkan angka tidak ada gunanya untuk diuji.
Ada masalah Anda yang sebenarnya di sana. Jika pengujian unit tampaknya sulit atau sia-sia maka kemungkinan karena cacat desain. Jika lebih berorientasi objek tanda tangan metode Anda tidak akan begitu besar dan akan ada lebih sedikit input yang mungkin untuk diuji.
Saya tidak perlu masuk ke OO saya lebih unggul dari pemrograman prosedural ...
sumber
Dalam pandangan saya, tes unit bahkan berguna di kelas adder kecil Anda: jangan berpikir tentang "pengodean ulang" algoritma dan menganggapnya sebagai kotak hitam dengan satu-satunya pengetahuan yang Anda miliki tentang perilaku fungsional (jika Anda terbiasa dengan multiplikasi cepat, Anda tahu beberapa upaya yang lebih cepat, tetapi lebih rumit daripada menggunakan "a * b") dan antarmuka publik Anda. Daripada Anda harus bertanya pada diri sendiri, "Apa yang salah?" ...
Dalam kebanyakan kasus, ini terjadi di perbatasan (saya melihat Anda menguji sudah menambahkan pola ini ++, -, + -, 00 - waktu untuk menyelesaikannya dengan - +, 0+, 0, 0, +0, -0). Pikirkan apa yang terjadi pada MAX_INT dan MIN_INT saat menambahkan atau mengurangi (menambahkan negatif;)) di sana. Atau coba pastikan tes Anda terlihat persis seperti apa yang terjadi pada dan sekitar nol.
Secara keseluruhan rahasianya sangat sederhana (mungkin untuk yang lebih rumit juga;)) untuk kelas sederhana: pikirkan kontrak kelas Anda (lihat desain dengan kontrak) dan kemudian uji terhadap mereka. Semakin baik Anda mengetahui inv, pre dan post Anda, "pelengkap" tes Anda akan.
Petunjuk untuk kelas tes Anda: cobalah untuk menulis hanya satu pernyataan dalam suatu metode. Berikan nama metode yang baik (mis. "TestAddingToMaxInt", "testAddingTwoNegatives") untuk mendapatkan umpan balik terbaik ketika pengujian Anda gagal setelah perubahan kode.
sumber
Daripada menguji untuk nilai pengembalian yang dihitung secara manual, atau menduplikasi logika dalam pengujian untuk menghitung nilai pengembalian yang diharapkan, uji nilai pengembalian untuk properti yang diharapkan.
Misalnya, jika Anda ingin menguji metode yang membalikkan matriks, Anda tidak ingin membalikkan nilai input Anda secara manual, Anda harus mengalikan nilai pengembalian dengan input dan memeriksa apakah Anda mendapatkan matriks identitas.
Untuk menerapkan pendekatan ini pada metode Anda, Anda harus mempertimbangkan tujuan dan semantiknya, untuk mengidentifikasi properti apa yang akan memiliki nilai pengembalian relatif terhadap input.
sumber
Tes unit adalah alat produktivitas. Anda menerima permintaan perubahan, mengimplementasikannya, lalu menjalankan kode Anda melalui unit test gambit. Pengujian otomatis ini menghemat waktu.
I feel that this test is totally useless, because you are required to manually compute the wanted result and test it, I feel like a better unit test here would be
Titik diperdebatkan. Tes dalam contoh hanya menunjukkan bagaimana membuat instance kelas dan menjalankannya melalui serangkaian tes. Berkonsentrasi pada hal-hal kecil dalam satu implementasi tidak ada hutan untuk pepohonan.
Can someone provide me with an example where unit testing is actually useful?
Anda memiliki entitas Karyawan. Entitas mengandung nama dan alamat. Klien memutuskan untuk menambahkan bidang ReportsTo.
Itu adalah tes dasar BL untuk bekerja dengan seorang karyawan. Kode akan lulus / gagal perubahan skema yang baru saja Anda buat. Ingatlah bahwa pernyataan bukan satu-satunya hal yang dilakukan tes. Menjalankan kode juga memastikan tidak ada pengecualian yang menggelembung.
Seiring waktu, memiliki tes di tempat membuatnya lebih mudah untuk membuat perubahan secara umum. Kode secara otomatis diuji untuk pengecualian dan terhadap Asersi yang Anda buat. Ini menghindari banyak overhead yang dikeluarkan oleh pengujian manual oleh grup QA. Sementara UI masih cukup sulit untuk diotomatisasi, lapisan lain umumnya sangat mudah dengan asumsi Anda menggunakan pengubah akses dengan benar.
I feel like the only unit testing I could do would be to simply re-code the algorithm in the test.
Bahkan logika prosedural mudah dirangkum dalam suatu fungsi. Enkapsulasi, instantiate, dan masukkan int / primitif untuk diuji (atau objek tiruan). Jangan salin tempel kode ke Tes Unit. Itu mengalahkan KERING. Itu juga mengalahkan tes sepenuhnya karena Anda tidak menguji kode, tetapi salinan kode. Jika kode yang seharusnya diuji mengalami perubahan, tes tetap berlalu!
sumber
Mengambil contoh Anda (dengan sedikit refactoring),
tidak membantu untuk:
math.add
berperilaku secara internal,Ini seperti mengatakan:
math.add
dapat berisi ratusan LOC; lihat di bawah).Ini juga berarti Anda tidak perlu menambahkan tes seperti:
Mereka tidak membantu, atau setidaknya, begitu Anda membuat pernyataan pertama, yang kedua tidak membawa apa-apa yang berguna.
Sebaliknya, bagaimana dengan:
Ini cukup jelas dan sangat membantu bagi Anda dan orang yang akan menjaga kode sumber nanti. Bayangkan orang ini melakukan sedikit modifikasi pada
math.add
untuk menyederhanakan kode dan mengoptimalkan kinerja, dan melihat hasil tes seperti:orang ini akan segera memahami bahwa metode yang baru dimodifikasi tergantung pada urutan argumen: jika argumen pertama adalah bilangan bulat dan yang kedua adalah angka yang panjang, hasilnya akan berupa bilangan bulat, sedangkan angka yang panjang diharapkan.
Dengan cara yang sama, memperoleh nilai aktual
4.141592
pada pernyataan pertama cukup jelas: Anda tahu bahwa metode ini diharapkan dapat menangani presisi besar , tetapi sebenarnya gagal.Untuk alasan yang sama, dua pernyataan berikut dapat masuk akal dalam beberapa bahasa:
Juga, bagaimana dengan:
Cukup jelas juga: Anda ingin metode Anda dapat menangani infinity dengan benar. Melampaui batas tanpa batas atau melempar pengecualian bukanlah perilaku yang diharapkan.
Atau mungkin, tergantung pada bahasa Anda, ini akan lebih masuk akal?
sumber
Untuk fungsi yang sangat sederhana seperti menambahkan, pengujian dapat dianggap tidak perlu, tetapi karena fungsi Anda menjadi lebih kompleks, itu menjadi semakin jelas mengapa pengujian diperlukan.
Pikirkan tentang apa yang Anda lakukan ketika Anda pemrograman (tanpa pengujian unit). Biasanya Anda menulis beberapa kode, menjalankannya, melihat bahwa itu berfungsi, dan beralih ke hal berikutnya, bukan? Ketika Anda menulis lebih banyak kode, terutama di sistem / GUI / situs web yang sangat besar, Anda merasa harus melakukan lebih banyak lagi "menjalankan dan melihat apakah itu berfungsi". Anda harus mencoba ini dan mencobanya. Kemudian, Anda membuat beberapa perubahan dan Anda harus mencoba hal-hal yang sama lagi. Menjadi sangat jelas bahwa Anda dapat menghemat waktu dengan menulis unit test yang akan mengotomatiskan seluruh bagian "berlari dan melihat apakah itu berfungsi".
Ketika proyek Anda menjadi semakin besar, semakin banyak hal yang harus Anda "jalankan dan lihat apakah itu berhasil" untuk menjadi tidak realistis. Jadi Anda akhirnya hanya menjalankan dan mencoba beberapa komponen utama dari GUI / proyek dan kemudian berharap semuanya baik-baik saja. Ini adalah resep untuk bencana. Tentu saja Anda, sebagai manusia, tidak dapat berulang kali menguji untuk setiap situasi yang mungkin digunakan klien Anda jika GUI digunakan oleh ratusan orang. Jika Anda memiliki tes unit di tempat, Anda bisa menjalankan tes sebelum mengirimkan versi stabil, atau bahkan sebelum melakukan ke repositori pusat (jika tempat kerja Anda menggunakannya). Dan, jika ada bug yang ditemukan di lain waktu, Anda bisa menambahkan unit test untuk memeriksanya di masa mendatang.
sumber
Salah satu manfaat dari penulisan unit test adalah membantu Anda menulis kode yang lebih kuat dengan memaksa Anda untuk berpikir tentang kasus tepi. Bagaimana dengan pengujian untuk beberapa kasus tepi, seperti integer overflow, pemotongan desimal, atau penanganan nulls untuk parameter?
sumber
Mungkin Anda menganggap add () diimplementasikan dengan instruksi ADD. Jika beberapa programmer junior atau insinyur perangkat keras mengimplementasikan kembali fungsi add () menggunakan ANDS / ORS / XORS, bit invert dan shift, Anda mungkin ingin mengujinya berdasarkan instruksi ADD.
Secara umum, jika Anda mengganti nyali add (), atau unit yang sedang diuji, dengan nomor acak atau generator keluaran, bagaimana Anda tahu bahwa ada sesuatu yang rusak? Encode pengetahuan itu dalam unit test Anda. Jika tidak ada yang tahu apakah itu rusak, maka cukup periksa beberapa kode untuk rand () dan pulanglah, pekerjaan Anda selesai.
sumber
Saya mungkin telah melewatkannya di antara semua balasan tetapi, bagi saya, penggerak utama di balik Unit Testing lebih sedikit tentang membuktikan kebenaran suatu metode hari ini tetapi itu membuktikan kelanjutan yang benar dari metode tersebut ketika Anda pernah mengubahnya .
Ambil fungsi sederhana, seperti mengembalikan jumlah item dalam beberapa koleksi. Hari ini, ketika daftar Anda didasarkan pada satu struktur data internal yang Anda ketahui dengan baik, Anda mungkin berpikir bahwa metode ini sangat jelas menyakitkan sehingga Anda tidak memerlukan tes untuk itu. Kemudian, dalam beberapa bulan atau tahun, Anda (atau orang lain ) memutuskan untuk mengganti struktur daftar internal. Anda masih perlu tahu bahwa getCount () mengembalikan nilai yang benar.
Di situlah Tes Unit Anda benar-benar menjadi milik mereka.
Anda dapat mengubah implementasi internal kode Anda tetapi untuk konsumen kode itu, hasilnya tetap sama.
sumber