Jika setiap jalur melalui program diuji, apakah itu menjamin menemukan semua bug?
Jika tidak, mengapa tidak? Bagaimana Anda bisa melalui setiap kombinasi yang mungkin dari aliran program dan tidak menemukan masalah jika ada?
Saya ragu untuk menyarankan bahwa "semua bug" dapat ditemukan, tetapi mungkin itu karena cakupan path tidak praktis (seperti kombinatorial) sehingga tidak pernah dialami?
Catatan: artikel ini memberikan ringkasan singkat tentang jenis cakupan seperti yang saya pikirkan.
Jawaban:
Tidak
Karena bahkan jika Anda menguji semua jalur yang mungkin , Anda masih belum mengujinya dengan semua nilai yang mungkin atau semua kemungkinan kombinasi nilai . Misalnya (kodesemu):
sumber
Selain jawaban Mason , ada juga masalah lain: cakupan tidak memberi tahu Anda kode apa yang diuji, ia memberi tahu Anda kode apa yang dieksekusi .
Bayangkan Anda memiliki testuite dengan cakupan jalur 100%. Sekarang hapus semua asersi dan jalankan testuite lagi. Voa, testuite masih memiliki cakupan jalur 100%, tetapi tidak ada tes sama sekali.
sumber
ON ERROR GOTO
juga jalan, seperti Cif(errno)
.Berikut adalah contoh sederhana untuk menyelesaikan masalah. Pertimbangkan algoritma pengurutan berikut (di Jawa):
Sekarang, mari kita coba:
Sekarang, pertimbangkan bahwa (A) panggilan khusus ini untuk
sort
mengembalikan hasil yang benar, (B) semua jalur kode telah dicakup oleh tes ini.Tapi, jelas, program itu sebenarnya tidak mengurutkan.
Oleh karena itu cakupan semua jalur kode tidak cukup untuk menjamin bahwa program tidak memiliki bug.
sumber
Pertimbangkan
abs
fungsi, yang mengembalikan nilai absolut suatu angka. Berikut ini adalah tes (Python, bayangkan beberapa kerangka uji):Implementasi ini benar, tetapi hanya mendapat cakupan kode 60%:
Implementasi ini salah, tetapi mendapat 100% cakupan kode:
sumber
def abs(x): if x == -3: return 3 else: return 0
Anda mungkin bisa menghilangkanelse: return 0
bagian dan mendapatkan cakupan 100%, tetapi fungsi ini pada dasarnya tidak akan berguna meskipun itu melewati unit test.Namun tambahan lain untuk jawaban Mason , perilaku program mungkin tergantung pada lingkungan runtime.
Kode berikut berisi Use-After-Free:
Kode ini adalah Perilaku Tidak Terdefinisi, tergantung pada konfigurasi (rilis | debug), OS dan kompiler akan menghasilkan perilaku yang berbeda. Tidak hanya cakupan jalur tidak akan menjamin bahwa Anda akan menemukan UAF, tetapi paket pengujian Anda biasanya tidak akan mencakup berbagai perilaku yang mungkin dari UAF yang bergantung pada konfigurasi.
Pada catatan lain, bahkan jika cakupan jalur adalah untuk menjamin menemukan semua bug, tidak mungkin hal itu dapat dicapai dalam praktiknya pada program apa pun. Pertimbangkan yang berikut ini:
Jika test-suite Anda dapat menghasilkan semua jalur untuk ini, maka selamat Anda adalah seorang cryptographer.
sumber
cryptohash
, agak sulit untuk mengatakan apa itu "cukup kecil". Mungkin butuh dua hari untuk menyelesaikan supercalculator. Tapi ya,int
mungkin sedikit berubahshort
.Jelas dari jawaban lain bahwa cakupan kode 100% dalam pengujian tidak berarti kebenaran kode 100%, atau bahkan semua bug yang dapat ditemukan dengan pengujian, akan ditemukan (apalagi bug yang tidak dapat dites oleh tes apa pun).
Cara lain untuk menjawab pertanyaan ini adalah dari praktik:
Ada, di dunia nyata, dan memang di komputer Anda sendiri, banyak perangkat lunak yang dikembangkan menggunakan serangkaian tes yang memberikan cakupan 100% dan yang masih memiliki bug, termasuk bug yang sebaiknya diidentifikasi oleh pengujian lebih baik.
Oleh karena itu pertanyaan yang disyaratkan, adalah:
Alat-alat cakupan kode membantu mengidentifikasi bidang-bidang yang telah diabaikan untuk diuji. Itu bisa baik-baik saja (kode terbukti benar bahkan tanpa pengujian) mungkin tidak dapat diselesaikan (untuk beberapa alasan jalur tidak dapat dipukul), atau dapat menjadi lokasi bug bau besar baik sekarang atau mengikuti modifikasi di masa depan.
Dalam beberapa hal, pemeriksa ejaan dapat dibandingkan: Sesuatu dapat "lulus" periksa ejaan dan salah eja sedemikian rupa sehingga cocok dengan kata dalam kamus. Atau bisa "gagal" karena kata-kata yang benar tidak ada dalam kamus. Atau bisa lewat dan menjadi omong kosong. Pemeriksaan ejaan adalah alat yang membantu Anda mengidentifikasi tempat-tempat yang mungkin Anda lewatkan dalam pembacaan bukti Anda, tetapi sama seperti itu tidak dapat menjamin pembacaan bukti yang lengkap dan benar, sehingga cakupan kode tidak dapat menjamin pengujian yang lengkap dan benar.
Dan tentu saja cara yang salah untuk menggunakan pemeriksaan ejaan adalah terkenal dengan setiap saran di laut yang disarankan sehingga hal yang merunduk menjadi lebih buruk maka jika kita meninggalkannya pinjaman.
Dengan cakupan kode bisa menggoda, terutama jika Anda memiliki 98% hampir sempurna, untuk mengisi kasus sehingga jalur yang tersisa terpukul.
Itu sama dengan meluruskan dengan pemeriksaan ejaan bahwa itu semua kata cuaca atau simpul itu semua kata yang tepat. Hasilnya berantakan.
Namun, jika Anda mempertimbangkan tes apa yang dibutuhkan jalur yang tidak tercakup, alat cakupan kode akan melakukan tugasnya; tidak dalam menjanjikan Anda kebenaran, tetapi itu menunjukkan beberapa pekerjaan yang perlu dilakukan.
sumber
Cakupan jalur tidak dapat memberi tahu Anda apakah semua fitur yang diperlukan telah diterapkan. Meninggalkan fitur adalah bug, tetapi cakupan jalur tidak akan mendeteksinya.
sumber
Bagian dari masalah adalah bahwa cakupan 100% hanya menjamin bahwa kode akan berfungsi dengan benar setelah satu eksekusi . Beberapa bug seperti kebocoran memori mungkin tidak terlihat atau menyebabkan masalah setelah satu eksekusi, tetapi seiring waktu akan menyebabkan masalah untuk aplikasi.
Misalnya, Anda memiliki aplikasi yang terhubung ke database. Mungkin dalam satu metode programmer lupa untuk menutup koneksi ke database ketika mereka selesai dengan permintaan mereka. Anda bisa menjalankan beberapa tes pada metode ini dan tidak menemukan kesalahan dengan fungsionalitasnya, tetapi server database Anda mungkin mengalami skenario di mana tidak ada koneksi yang tersedia karena metode khusus ini tidak menutup koneksi ketika itu dilakukan dan koneksi terbuka harus sekarang batas waktu.
sumber
times_two(x) = x + 2
, ini akan sepenuhnya ditanggung oleh test suiteassert(times_two(2) == 4)
, tetapi ini masih jelas kode kereta! Tidak perlu kebocoran memori :)Seperti yang sudah dikatakan, jawabannya adalah TIDAK.
Selain apa yang dikatakan, ada bug yang muncul di tingkat yang berbeda, yang tidak dapat diuji dengan tes unit. Untuk menyebutkan beberapa saja:
sumber
Apa artinya bagi setiap jalur untuk diuji?
Jawaban lainnya bagus, tetapi saya hanya ingin menambahkan bahwa kondisi "setiap jalur melalui program diuji" itu sendiri tidak jelas.
Pertimbangkan metode ini:
Jika Anda menulis tes yang menegaskan
add(1, 2) == 3
, alat cakupan kode akan memberi tahu Anda bahwa setiap baris dilakukan. Tetapi Anda belum benar-benar menyatakan apa pun tentang efek samping global atau tugas yang tidak berguna. Garis-garis itu dieksekusi, tetapi belum benar-benar diuji.Pengujian mutasi akan membantu menemukan masalah seperti ini. Alat pengujian mutasi akan memiliki daftar cara yang telah ditentukan sebelumnya untuk "mengubah" kode dan melihat apakah tes masih lulus. Sebagai contoh:
+=
ke-=
. Mutasi itu tidak akan menyebabkan kegagalan tes, jadi itu akan membuktikan bahwa tes Anda tidak menyatakan sesuatu yang berarti tentang efek samping global.Pada dasarnya, tes mutasi adalah cara untuk menguji tes Anda . Tapi sama seperti Anda tidak akan pernah menguji fungsi aktual dengan setiap set input yang mungkin, Anda tidak akan pernah menjalankan setiap mutasi yang mungkin, jadi sekali lagi, ini terbatas.
Setiap tes yang dapat kita lakukan adalah heuristik untuk bergerak menuju program bebas bug. Tidak ada yang sempurna.
sumber
Yah ... ya sebenarnya, jika setiap jalur "melalui" program ini diuji. Tetapi itu berarti, setiap jalur yang mungkin melalui seluruh ruang dari semua status yang mungkin dimiliki program, termasuk semua variabel. Bahkan untuk program yang dikompilasi secara statis sangat sederhana - katakanlah, pembuat angka Fortran lama - itu tidak layak, meskipun setidaknya dapat dibayangkan: jika Anda hanya memiliki dua variabel integer, Anda pada dasarnya berurusan dengan semua cara yang mungkin untuk menghubungkan titik-titik pada kisi dua dimensi; sebenarnya sangat mirip dengan Travelling Salesman. Untuk n variabel seperti itu, Anda berhadapan dengan ruang n- dimensi, jadi untuk setiap program nyata, tugas tersebut sama sekali tidak dapat dilakukan.
Lebih buruk: untuk hal-hal serius, Anda tidak hanya memiliki sejumlah variabel primitif, tetapi membuat variabel dengan cepat dalam panggilan fungsi, atau memiliki variabel ukuran variabel ... atau hal-hal seperti itu, mungkin dalam bahasa lengkap Turing. Itu membuat ruang ruang dimensi tak terbatas, menghancurkan semua harapan cakupan penuh, bahkan diberikan peralatan pengujian yang sangat kuat.
Yang mengatakan ... sebenarnya hal-hal tidak begitu suram. Hal ini dimungkinkan untuk proove seluruh program untuk menjadi benar, tetapi Anda harus menyerah beberapa ide.
Pertama: sangat disarankan untuk beralih ke bahasa deklaratif. Bahasa imperatif, untuk beberapa alasan, selalu menjadi yang paling populer, tetapi cara mereka menggabungkan algoritma dengan interaksi dunia nyata membuatnya sangat sulit untuk mengatakan apa yang Anda maksud dengan "benar".
Jauh lebih mudah dalam bahasa pemrograman yang benar-benar fungsional : ini memiliki perbedaan yang jelas antara sifat-sifat fungsi matematika yang sangat menarik , dan interaksi dunia nyata yang tidak dapat Anda katakan. Untuk fungsi, sangat mudah untuk menentukan "perilaku yang benar": jika untuk semua input yang mungkin (dari tipe argumen) hasil yang diinginkan keluar, maka fungsi tersebut berperilaku dengan benar.
Sekarang, Anda mengatakan itu masih sulit dilakukan ... setelah semua, ruang dari semua argumen yang mungkin pada umumnya juga tak terbatas-dimensi. Benar - meskipun untuk satu fungsi, bahkan pengujian cakupan naif membawa Anda lebih jauh dari yang Anda bisa harapkan dalam program penting! Namun, ada alat yang luar biasa kuat yang mengubah permainan: kuantifikasi universal / polimorfisme parametrik . Pada dasarnya, ini memungkinkan Anda untuk menulis fungsi pada jenis data yang sangat umum, dengan jaminan bahwa jika itu berfungsi untuk contoh data yang sederhana, ia akan bekerja untuk setiap input yang mungkin sama sekali.
Setidaknya secara teoritis. Tidak mudah menemukan jenis yang tepat yang sangat umum sehingga Anda dapat sepenuhnya membuktikan hal ini - biasanya, Anda memerlukan bahasa yang diketik secara dependen , dan ini cenderung agak sulit untuk digunakan. Tetapi menulis dengan gaya fungsional dengan polimorfisme parametrik saja sudah meningkatkan "tingkat keamanan" Anda dengan sangat baik - Anda tidak perlu menemukan semua bug, tetapi Anda harus menyembunyikannya dengan baik sehingga kompiler tidak melihatnya!
sumber