Mengapa tes unit gagal dilihat sebagai buruk?

93

Di beberapa organisasi, tampaknya, bagian dari proses rilis perangkat lunak adalah dengan menggunakan pengujian unit, tetapi pada setiap titik waktu semua tes unit harus lulus. Misalnya mungkin ada beberapa layar yang menunjukkan semua tes unit lulus berwarna hijau - yang seharusnya bagus.

Secara pribadi, saya pikir ini bukan seperti yang seharusnya karena alasan berikut:

  1. Ini mempromosikan gagasan bahwa kode harus sempurna dan tidak ada bug yang seharusnya ada - yang di dunia nyata jelas tidak mungkin untuk program dengan ukuran berapa pun.

  2. Disinsentif untuk memikirkan unit test yang akan gagal. Atau tentu saja muncul dengan unit test yang akan sulit untuk diperbaiki.

  3. Jika pada suatu saat semua tes unit lulus, maka tidak ada gambaran besar dari keadaan perangkat lunak pada suatu titik waktu. Tidak ada roadmap / tujuan.

  4. Ini menghalangi tes unit menulis di muka - sebelum implementasi.

Saya bahkan akan menyarankan bahwa bahkan merilis perangkat lunak dengan tes unit gagal tidak perlu buruk. Setidaknya Anda tahu bahwa beberapa aspek dari perangkat lunak memiliki keterbatasan.

Apakah saya melewatkan sesuatu di sini? Mengapa organisasi mengharapkan semua tes unit lulus? Bukankah ini hidup di dunia mimpi? Dan bukankah itu benar-benar menghalangi pemahaman kode sebenarnya?

pengguna619818
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
maple_shaft

Jawaban:

270

Pertanyaan ini mengandung beberapa kesalahpahaman IMHO, tetapi yang utama yang ingin saya fokuskan adalah bahwa ia tidak membedakan antara cabang pengembangan lokal, trunk, staging atau rilis cabang.

Di cabang dev setempat, kemungkinan akan mengalami beberapa tes unit yang gagal hampir setiap saat. Dalam bagasi, itu hanya dapat diterima pada tingkat tertentu, tetapi sudah merupakan indikator yang kuat untuk memperbaiki hal-hal secepatnya. Perhatikan bahwa kegagalan unit test di bagasi dapat mengganggu anggota tim lainnya, karena mereka mengharuskan setiap orang untuk memeriksa apakah perubahan terbaru yang menyebabkan kegagalan.

Dalam cabang pementasan atau rilis, tes gagal adalah "peringatan merah", menunjukkan ada yang salah dengan beberapa perubahan, ketika digabungkan dari bagasi ke cabang rilis.

Saya bahkan akan menyarankan bahwa bahkan merilis perangkat lunak dengan tes unit gagal tidak perlu buruk.

Melepaskan perangkat lunak dengan beberapa bug yang dikenal di bawah tingkat keparahan tertentu tidak selalu buruk. Namun, gangguan yang diketahui ini seharusnya tidak menyebabkan tes unit gagal. Jika tidak, setelah setiap unit tes dijalankan, seseorang harus melihat ke dalam 20 unit test yang gagal dan memeriksa satu per satu apakah kegagalannya dapat diterima atau tidak. Ini menjadi rumit, rawan kesalahan, dan membuang sebagian besar aspek otomatisasi dari tes unit.

Jika Anda benar-benar memiliki tes untuk bug yang dapat diterima dan diketahui, gunakan fitur menonaktifkan / mengabaikan alat pengujian unit Anda (sehingga Anda tidak dijalankan secara default, hanya berdasarkan permintaan). Selain itu, tambahkan tiket prioritas rendah ke pelacak masalah Anda, sehingga masalahnya tidak akan terlupakan.

Doc Brown
sumber
18
Saya pikir ini adalah jawaban yang sebenarnya. OP menyebutkan "proses rilis" dan "beberapa layar [menampilkan hasil tes]", yang terdengar seperti server build. Rilis tidak sama dengan pengembangan (jangan berkembang dalam produksi!); tidak apa-apa memiliki tes gagal di dev, mereka seperti TODO; semuanya harus berwarna hijau (SELESAI) saat didorong ke server build.
Warbo
7
Jawaban yang jauh lebih baik daripada yang tertinggi. Ini menunjukkan pemahaman dari mana op berasal dari tanpa menguliahi mereka tentang beberapa situasi dunia ideal, mengakui kemungkinan bug yang diketahui (yang tidak seluruh peta jalan dijatuhkan untuk memperbaiki beberapa kasus sudut langka) dan menjelaskan bahwa unit test hanya harus pasti menjadi hijau di cabang rilis / proses.
Sebastiaan van den Broek
5
@SebastiaanvandenBroek: terima kasih atas balasan positif Anda. Hanya untuk memperjelas ini: IMHO gagal unit test harus langka bahkan di bagasi, karena mendapatkan kegagalan seperti itu terlalu sering akan mengganggu seluruh tim, bukan hanya orang yang melakukan perubahan yang menyebabkan kegagalan.
Doc Brown
4
Saya pikir masalahnya di sini adalah berpikir semua tes otomatis adalah tes unit. Banyak kerangka kerja pengujian mencakup kemampuan untuk menandai tes yang diperkirakan gagal (sering disebut XFAIL). (Ini berbeda dari tes yang membutuhkan hasil kesalahan. Tes XFAIL idealnya akan berhasil, tetapi tidak.) Paket tes masih lulus dengan gagal ini. Kasus penggunaan yang paling umum adalah hal-hal yang hanya gagal pada beberapa platform (dan hanya XFAIL pada mereka), tetapi menggunakan fitur untuk melacak sesuatu yang akan membutuhkan terlalu banyak pekerjaan untuk memperbaikinya saat ini juga masuk akal. Tapi tes semacam ini biasanya bukan tes unit.
Kevin Cathcart
1
+1, meskipun saya menyarankan sedikit tambahan (dalam huruf tebal) untuk kalimat ini: "Ini menjadi rumit, rawan kesalahan, kondisi orang untuk mengabaikan kegagalan dalam test suite sebagai kebisingan , dan membuang sebagian besar aspek otomatisasi unit test .
mtraceur
228

... semua tes unit lulus berwarna hijau - yang seharusnya bagus.

Ini adalah baik. Tidak "seharusnya" tentang itu.

Ini mempromosikan gagasan bahwa kode harus sempurna dan tidak ada bug yang seharusnya ada - yang di dunia nyata jelas tidak mungkin untuk program dengan ukuran berapa pun.

Tidak. Ini membuktikan bahwa Anda telah menguji kodenya sebaik yang Anda bisa hingga saat ini. Sangat mungkin bahwa tes Anda tidak mencakup setiap kasus. Jika demikian, kesalahan apa pun pada akhirnya akan muncul dalam laporan bug dan Anda akan menulis tes [gagal] untuk mereproduksi masalah dan kemudian memperbaiki aplikasi sehingga tes lulus.

Disinsentif untuk memikirkan unit test yang akan gagal.

Tes yang gagal atau negatif memberi batasan tegas pada apa yang akan dan tidak akan diterima aplikasi Anda. Sebagian besar program yang saya tahu akan menolak "tanggal" tanggal 30 Februari. Juga, Pengembang, tipe kreatif kita, tidak ingin mematahkan "bayi mereka". Fokus yang dihasilkan pada kasus "happy-path" mengarah ke aplikasi rapuh yang sering rusak.

Untuk membandingkan pola pikir Pengembang dan Penguji:

  • Pengembang berhenti segera setelah kode melakukan apa yang mereka inginkan.
  • Penguji berhenti ketika mereka tidak lagi dapat membuat kode rusak.

Ini adalah perspektif yang sangat berbeda dan sulit bagi banyak Pengembang untuk melakukan rekonsiliasi.

Atau tentu saja muncul dengan unit test yang akan sulit untuk diperbaiki.

Anda tidak menulis tes untuk bekerja sendiri. Anda menulis tes untuk memastikan bahwa kode Anda melakukan apa yang seharusnya dilakukan dan, yang lebih penting, bahwa itu terus melakukan apa yang seharusnya dilakukan setelah Anda mengubah implementasi internalnya.

  • Debugging "membuktikan" bahwa kode melakukan apa yang Anda inginkan hari ini .
  • Tes "membuktikan" bahwa kode masih melakukan apa yang Anda inginkan dari waktu ke waktu .

Jika pada suatu saat semua tes unit lulus, maka tidak ada gambaran besar dari keadaan perangkat lunak pada suatu titik waktu. Tidak ada roadmap / tujuan.

Satu-satunya "gambar" pengujian memberi Anda adalah snapshot bahwa kode "bekerja" pada titik waktu itu diuji. Bagaimana itu berkembang setelah itu adalah cerita yang berbeda.

Ini menghalangi tes unit menulis di muka - sebelum implementasi.

Itulah yang seharusnya Anda lakukan. Tulis tes yang gagal (karena metode pengujiannya belum diimplementasikan) kemudian tulis kode metode untuk membuat metode ini bekerja dan, karenanya, lulus uji. Itulah inti dari Pengembangan Berbasis Tes.

Saya bahkan akan menyarankan bahwa bahkan merilis perangkat lunak dengan tes unit gagal tidak perlu buruk. Setidaknya Anda tahu bahwa beberapa aspek dari perangkat lunak memiliki keterbatasan.

Melepaskan kode dengan tes yang rusak berarti bahwa beberapa bagian dari fungsinya tidak lagi berfungsi seperti sebelumnya. Itu mungkin tindakan yang disengaja karena Anda telah memperbaiki bug atau meningkatkan fitur (tetapi kemudian Anda harus mengubah tes terlebih dahulu sehingga gagal, lalu mengkodekan perbaikan / peningkatan, membuat tes bekerja dalam proses). Lebih penting lagi: kita semua adalah Manusia dan kita membuat kesalahan. Jika Anda memecahkan kode, maka Anda harus memecahkan tes dan tes yang rusak harus mengatur bel alarm berbunyi.

Bukankah ini hidup di dunia mimpi?

Jika ada, itu hidup di Dunia Nyata , mengakui bahwa Pengembang tidak mahatahu atau tidak salah, bahwa kita memang membuat kesalahan dan bahwa kita membutuhkan jaring pengaman untuk menangkap kita jika dan ketika kita memang berantakan!
Masukkan Tes.

Dan bukankah itu benar-benar menghalangi pemahaman kode sebenarnya?

Mungkin. Anda tidak perlu memahami implementasi sesuatu untuk menulis tes untuk itu (itu bagian dari titik mereka). Tes menentukan perilaku dan batasan aplikasi dan memastikan bahwa mereka tetap sama kecuali Anda sengaja mengubahnya.

Phill W.
sumber
7
@ Tibo: Menonaktifkan tes seperti mengomentari suatu fungsi. Anda memiliki kontrol versi. Gunakan.
Kevin
6
@Kevin Saya tidak tahu apa yang Anda maksud dengan 'gunakan'. Saya menandai tes sebagai 'dilewati' atau 'tertunda' atau konvensi apa pun yang digunakan pelari uji saya, dan komit tag yang dilewati untuk kontrol versi.
mulai
4
@dcorking: Maksud saya jangan komentar kode keluar, hapus. Jika nanti Anda memutuskan bahwa Anda membutuhkannya, maka pulihkan dari kontrol versi. Melakukan tes yang dinonaktifkan tidak berbeda.
Kevin
4
"Sangat mungkin bahwa tes Anda tidak mencakup setiap kasus." Saya akan mengatakan sejauh ini untuk setiap potongan kode non-sepele yang diuji, Anda pasti tidak memiliki setiap kasus yang dicakup.
corsiKa
6
@ Tibo Pendukung pengujian unit mengatakan bahwa waktu siklus dari menulis tes gagal untuk menulis kode untuk itu harus kecil (misalnya 20 menit. Beberapa mengklaim 30 detik). Jika Anda tidak punya waktu untuk menulis kode dengan segera, itu mungkin terlalu rumit. Jika tidak rumit, hapus tes karena dapat ditulis ulang jika fitur yang dijatuhkan ditambahkan lagi. Mengapa tidak berkomentar? Anda tidak tahu bahwa fitur tersebut akan ditambahkan lagi, jadi tes yang dikomentari (atau kode) hanyalah noise.
CJ Dennis
32

Mengapa tes unit gagal dilihat sebagai buruk?

Mereka tidak - pengembangan tes didorong dibangun di atas gagasan gagal tes. Tes unit gagal untuk mendorong pengembangan, gagal tes penerimaan untuk mendorong sebuah cerita ....

Yang Anda lewatkan adalah konteks ; di mana tes unit diizinkan gagal?

Jawaban yang biasa adalah bahwa tes unit diperbolehkan gagal hanya di kotak pasir pribadi.

Gagasan dasarnya adalah ini: di lingkungan di mana tes yang gagal dibagikan, dibutuhkan upaya ekstra untuk memahami apakah perubahan pada kode produksi telah menyebabkan kesalahan baru. Perbedaan antara nol dan bukan nol jauh lebih mudah untuk dideteksi dan dikelola daripada perbedaan antara N dan bukan N.

Selain itu, menjaga kode bersama bersih berarti bahwa pengembang dapat tetap mengerjakan tugas. Ketika saya menggabungkan kode Anda, saya tidak perlu mengubah konteks dari masalah yang saya bayar untuk menyelesaikan untuk mengkalibrasi pemahaman saya tentang berapa banyak tes yang harus gagal. Jika kode bersama melewati semua tes, setiap kegagalan yang muncul ketika saya menggabungkan perubahan saya harus menjadi bagian dari interaksi antara kode saya dan baseline bersih yang ada.

Demikian pula, selama naik pesawat, pengembang baru dapat menjadi lebih cepat produktif, karena mereka tidak perlu menghabiskan waktu untuk menemukan tes gagal mana yang "dapat diterima".

Untuk lebih tepatnya: disiplinnya adalah tes yang dijalankan selama build harus lulus.

Ada, seperti yang bisa saya katakan, tidak ada yang salah dengan memiliki tes gagal yang dinonaktifkan .

Misalnya, dalam lingkungan "integrasi berkelanjutan", Anda akan berbagi kode pada irama tinggi. Integrasi sering kali tidak berarti bahwa perubahan Anda harus siap dirilis. Ada berbagai macam teknik penyebaran gelap yang mencegah lalu lintas dirilis ke bagian kode sampai mereka siap.

Teknik-teknik yang sama dapat digunakan untuk menonaktifkan tes gagal juga.

Salah satu latihan yang saya lalui pada rilis poin adalah berurusan dengan pengembangan produk dengan banyak tes gagal. Jawaban yang kami ajukan adalah hanya melalui suite, menonaktifkan tes yang gagal dan mendokumentasikan masing-masing. Itu memungkinkan kami untuk dengan cepat mencapai titik di mana semua tes diaktifkan berlalu, dan manajemen / donor tujuan / pemilik emas semua bisa melihat perdagangan apa yang telah kami lakukan untuk sampai ke titik itu, dan dapat membuat keputusan tentang pembersihan dan pekerjaan baru.

Singkatnya: ada teknik lain untuk melacak pekerjaan yang tidak dilakukan selain meninggalkan banyak tes yang gagal di menjalankan suite.

VoiceOfUnasonason
sumber
Saya akan mengatakan "Tidak ... tidak ada yang salah dengan memiliki gagal tes yang yang cacat ".
CJ Dennis
Perubahan itu tentu saja menjelaskan artinya. Terima kasih.
VoiceOfUnreason
26

Ada banyak jawaban bagus, tetapi saya ingin menambahkan sudut lain yang saya yakini belum tercakup dengan baik: apa gunanya melakukan tes.

Tes unit tidak ada untuk memeriksa apakah kode Anda bebas bug.

Saya pikir ini adalah kesalahpahaman utama. Jika ini adalah peran mereka, Anda tentu berharap akan gagal tes di semua tempat. Melainkan,

Tes unit memeriksa apakah kode Anda melakukan apa yang Anda pikirkan.

Dalam kasus ekstrem, ini mungkin termasuk memeriksa bahwa bug yang diketahui tidak diperbaiki. Intinya adalah untuk memiliki kontrol atas basis kode Anda dan menghindari perubahan yang tidak disengaja. Ketika Anda melakukan perubahan itu baik-baik saja dan sebenarnya diharapkan untuk memecahkan beberapa tes - Anda mengubah perilaku kode. Tes yang baru saja rusak sekarang merupakan jejak bagus dari apa yang Anda ubah. Periksa apakah semua kerusakan sesuai dengan yang Anda inginkan dari perubahan Anda. Jika demikian, perbarui saja tes dan lanjutkan. Jika tidak - yah, kode baru Anda pasti bermasalah, kembali dan perbaiki sebelum mengirimkan!

Sekarang, semua hal di atas hanya berfungsi jika semua tes berwarna hijau, memberikan hasil positif yang kuat: beginilah cara kode bekerja. Tes merah tidak memiliki properti itu. "Ini yang tidak dilakukan kode ini" jarang merupakan informasi yang berguna.

Tes penerimaan mungkin apa yang Anda cari.

Ada yang namanya pengujian penerimaan. Anda dapat menulis serangkaian tes yang harus dipenuhi untuk memanggil tonggak sejarah berikutnya. Ini ok untuk menjadi merah, karena itulah mereka dirancang untuk itu. Tetapi mereka sangat berbeda dari unit test dan tidak bisa atau tidak harus menggantikannya.

Frax
sumber
2
Saya pernah harus mengganti perpustakaan dengan yang lain. Tes unit membantu saya memastikan bahwa semua kasing sudut masih diperlakukan secara identik dengan kode baru.
Thorbjørn Ravn Andersen
24

Saya melihatnya sebagai perangkat lunak yang setara dengan sindrom jendela pecah .

Tes kerja memberi tahu saya bahwa kode tersebut berkualitas dan pemilik kode peduli.

Adapun ketika Anda harus peduli dengan kualitas, itu lebih tergantung pada apa kode cabang / repositori yang sedang Anda kerjakan. Kode pengembangan mungkin telah melanggar tes yang mengindikasikan pekerjaan sedang berlangsung (mudah-mudahan!).

Tes yang rusak pada cabang / repositori untuk sistem live harus segera mengatur dering bel alarm. Jika tes yang rusak dibiarkan terus gagal atau jika itu secara permanen ditandai sebagai "abaikan" - perkirakan jumlahnya akan meningkat seiring waktu. Jika ini tidak ditinjau secara teratur preseden akan ditetapkan bahwa tidak apa-apa jika tes yang rusak dibiarkan.

Tes yang rusak dipandang sangat merendahkan di banyak toko sehingga memiliki batasan apakah kode yang rusak dapat dilakukan .

Robbie Dee
sumber
9
Jika tes mendokumentasikan seperti apa sistemnya, mereka tentu harus selalu lulus - jika tidak, itu berarti invarian rusak. Tetapi jika mereka mendokumentasikan cara sistem seharusnya , gagal tes dapat memiliki penggunaannya juga - selama kerangka pengujian unit Anda mendukung cara yang baik untuk menandai mereka sebagai "masalah yang diketahui", dan jika Anda menautkannya dengan item di pelacak masalah Anda. Saya pikir kedua pendekatan memiliki kelebihan mereka.
Luaan
1
@Luaan Ya, ini lebih tepatnya mengasumsikan bahwa semua unit test dibuat sama. Tentunya tidak jarang bagi manajer membangun untuk mengiris dan memotong tes melalui beberapa atribut tergantung pada berapa lama mereka berjalan, seberapa rapuh mereka dan berbagai kriteria lainnya.
Robbie Dee
Jawaban ini sangat bagus dari pengalaman saya sendiri. Setelah beberapa orang terbiasa mengabaikan sekelompok tes yang gagal, atau untuk melanggar praktik terbaik di beberapa titik, biarkan beberapa bulan berlalu dan Anda akan melihat% dari tes yang diabaikan meningkat secara dramatis, kualitas kode turun ke level "hack-script" . Dan akan sangat sulit untuk mengingat semua orang pada prosesnya.
usr-local-ΕΨΗΕΛΩΝ
11

Berikut adalah kesalahan logis yang mendasarinya:

Jika itu baik ketika semua tes lulus, maka itu pasti buruk jika ada tes gagal.

Dengan unit test, itu IS baik ketika semua tes lulus. Hal ini JUGA BAIK ketika tes gagal. Keduanya tidak perlu dalam oposisi.

Tes gagal adalah masalah yang ditangkap oleh alat Anda sebelum mencapai pengguna. Ini adalah kesempatan untuk memperbaiki kesalahan sebelum dipublikasikan. Dan itu hal yang baik.

Joel Coehoorn
sumber
Garis pemikiran yang menarik. Saya melihat kekeliruan pertanyaan lebih seperti ini: "karena bagus ketika tes unit gagal, itu buruk ketika semua tes lulus".
Doc Brown
Meskipun paragraf terakhir Anda adalah poin yang baik, tampaknya masalahnya lebih pada kesalahpahaman "pada titik waktu semua tes unit harus lulus" (seperti yang diterima jawaban menunjukkan) dan titik tes unit.
Dukeling
9

Jawaban Phill W bagus. Saya tidak bisa menggantinya.

Namun, saya ingin fokus pada bagian lain yang mungkin menjadi bagian dari kebingungan.

Di beberapa organisasi, tampaknya, bagian dari proses rilis perangkat lunak adalah menggunakan pengujian unit, tetapi pada setiap titik waktu semua tes unit harus lulus

"Kapan saja" ini melebih-lebihkan kasus Anda. Yang penting adalah bahwa unit test lulus setelah perubahan tertentu diterapkan, sebelum Anda mulai menerapkan perubahan lain.
Ini adalah cara Anda melacak perubahan yang menyebabkan bug muncul. Jika unit test mulai gagal setelah menerapkan perubahan 25 tetapi sebelum menerapkan perubahan 26, maka Anda tahu bahwa perubahan 25 menyebabkan bug.

Selama implementasi perubahan, tentu saja unit test bisa gagal; sangat tergantung pada seberapa besar perubahan itu. Jika saya mengembangkan kembali fitur inti, yang lebih dari sekadar perubahan kecil, saya mungkin akan mematahkan tes untuk sementara waktu sampai saya selesai mengimplementasikan versi baru dari logika.


Ini dapat membuat konflik sebagai aturan tim. Saya sebenarnya mengalami ini beberapa minggu yang lalu:

  • Setiap commit / push menyebabkan build. Build tidak boleh gagal (jika gagal atau ada pengujian yang gagal, pengembang yang berkomitmen disalahkan).
  • Setiap pengembang diharapkan untuk mendorong perubahan mereka (bahkan jika tidak lengkap) pada akhir hari, sehingga pimpinan tim dapat meninjau kode di pagi hari.

Entah aturan akan baik-baik saja. Namun kedua aturan tersebut tidak dapat bekerja bersama. Jika saya diberi perubahan besar yang membutuhkan beberapa hari untuk menyelesaikan, saya tidak akan dapat mematuhi kedua aturan secara bersamaan. Kecuali jika saya akan mengomentari perubahan saya setiap hari dan hanya melakukannya tanpa komentar setelah semuanya dilakukan; yang hanya kerja tidak masuk akal.

Dalam skenario ini, masalah di sini bukanlah bahwa tes unit tidak memiliki tujuan; itu adalah bahwa perusahaan memiliki harapan yang tidak realistis . Aturan sewenang-wenang mereka tidak mencakup semua kasus, dan kegagalan untuk mematuhi aturan secara membabi buta dianggap sebagai kegagalan pengembang daripada kegagalan aturan (yang, dalam kasus saya).

Flater
sumber
3
Salah satu cara ini dapat bekerja adalah dengan menggunakan percabangan, sehingga devs melakukan dan mendorong ke fitur cabang yang tidak perlu membangun dengan bersih sementara tidak lengkap, tetapi melakukan ke cabang inti memang memicu membangun, yang harus membangun dengan bersih.
Gwyn Evans
1
Menegakkan mendorong perubahan yang tidak lengkap adalah tidak masuk akal, saya tidak dapat melihat pembenaran untuk melakukannya. Mengapa tidak meninjau kode ketika perubahan selesai?
Callum Bradbury
Sebagai contoh, ini adalah cara cepat untuk memastikan bahwa kode tersebut tidak hanya pada laptop / workstation dev jika hard disk mereka berhenti bekerja atau hilang - jika ada kebijakan untuk melakukan komitmen bahkan jika di tengah kerja, maka ada sejumlah pekerjaan terbatas yang berisiko.
Gwyn Evans
1
Bendera fitur memperbaiki paradoks yang tampak.
RubberDuck
1
@Flater ya, untuk mengerjakan ulang logika yang ada juga.
RubberDuck
6

Jika Anda tidak memperbaiki semua tes unit, Anda dapat dengan cepat masuk ke kondisi di mana tidak ada yang memperbaiki tes yang rusak.

  1. Tidak benar karena lulus tes unit tidak menunjukkan kode sempurna

  2. Merupakan disinsentif untuk menghasilkan kode yang sulit untuk diuji juga, yang bagus dari sudut pandang desain

  3. Cakupan kode dapat membantu di sana (meskipun itu bukan obat mujarab). Juga tes unit hanyalah salah satu aspek pengujian - Anda juga ingin tes integrasi / penerimaan.

jk.
sumber
6

Untuk menambahkan beberapa poin ke jawaban yang sudah bagus ...

tetapi pada setiap titik waktu semua unit test harus lulus

Ini menunjukkan kurangnya pemahaman tentang proses rilis. Kegagalan pengujian dapat menunjukkan fitur yang direncanakan di bawah TDD yang belum diimplementasikan; atau mungkin mengindikasikan masalah yang diketahui yang memiliki perbaikan yang direncanakan untuk rilis di masa mendatang; atau mungkin hanya sesuatu di mana manajemen telah memutuskan ini tidak cukup penting untuk diperbaiki karena pelanggan tidak mungkin memperhatikan. Hal utama dari semua bagian ini adalah bahwa manajemen telah membuat keputusan tentang kegagalan.

Ini mempromosikan gagasan bahwa kode harus sempurna dan tidak ada bug yang seharusnya ada - yang di dunia nyata jelas tidak mungkin untuk program dengan ukuran berapa pun.

Jawaban lain telah mencakup batas pengujian.

Saya tidak mengerti mengapa Anda berpikir menghilangkan bug adalah kerugian. Jika Anda tidak ingin mengirimkan kode yang telah Anda periksa (sesuai kemampuan Anda) melakukan apa yang seharusnya, mengapa Anda bahkan bekerja dalam perangkat lunak?

Jika pada suatu saat semua tes unit lulus, maka tidak ada gambaran besar dari keadaan perangkat lunak pada suatu titik waktu. Tidak ada roadmap / tujuan.

Mengapa harus ada peta jalan?

Tes unit pada awalnya memeriksa apakah fungsionalitas berfungsi, tetapi kemudian (sebagai tes regresi) periksa apakah Anda tidak sengaja merusak apa pun. Untuk semua fitur dengan tes unit yang ada, tidak ada peta jalan . Setiap fitur diketahui berfungsi (dalam batas pengujian). Jika kode itu selesai, ia tidak memiliki peta jalan karena tidak perlu lagi bekerja.

Sebagai insinyur profesional, kita perlu menghindari jebakan pelapisan emas. Penggemar dapat membuang waktu untuk bermain-main dengan sesuatu yang berhasil. Sebagai profesional, kita perlu mengirimkan produk. Itu berarti kami membuat sesuatu berfungsi, memverifikasi bahwa itu berfungsi, dan beralih ke pekerjaan berikutnya.

Graham
sumber
6

Ini mempromosikan gagasan bahwa kode harus sempurna dan tidak ada bug yang seharusnya ada - yang di dunia nyata jelas tidak mungkin untuk program dengan ukuran berapa pun.

Tidak benar. mengapa Anda pikir itu tidak mungkin? di sini contoh untuk program yang berfungsi:

public class MyProgram {
  public boolean alwaysTrue() {
    return true;
  }

  @Test
  public void testAlwaysTrue() {
    assert(alwaysTrue() == true);
  }
}

Disinsentif untuk memikirkan unit test yang akan gagal. Atau tentu saja muncul dengan unit test yang akan sulit untuk diperbaiki.

Dalam hal ini mungkin bukan tes unit, tetapi tes integrasi jika rumit

Jika pada suatu saat semua tes unit lulus, maka tidak ada gambaran besar dari keadaan perangkat lunak pada suatu titik waktu. Tidak ada roadmap / tujuan.

benar, ini disebut unit test karena suatu alasan, itu memeriksa unit kode kecil.

Ini menghalangi tes unit menulis di muka - sebelum implementasi.

Pengembang akanmencegah menulis tes apa pun jika mereka tidak memahami manfaatnyamenurut sifatnya (kecuali jika mereka datang dari QA)

pengguna7294900
sumber
"Pengembang akan mencegah [sic] menulis tes apa pun berdasarkan sifatnya" - itu omong kosong. Saya bekerja di seluruh perusahaan pengembang yang berlatih TDD dan BDD.
RubberDuck
@RubberDuck saya mencoba menjawab "fakta" yang dimaksud dan saya melebih-lebihkan. Saya akan memperbarui
user7294900
"X akan dicegah melakukan Y jika mereka tidak memahami manfaat Y" berlaku untuk hampir semua X dan Y, sehingga pernyataan itu mungkin tidak terlalu berguna. Mungkin akan lebih masuk akal untuk menjelaskan manfaat dari menulis tes, dan khususnya melakukannya di muka.
Dukeling
2
"Tidak mungkin untuk program dengan ukuran berapa pun" tidak berarti "semua program, tidak peduli apa ukurannya", itu berarti "setiap program yang signifikan (memiliki panjang non-sepele)" Percontohan contoh Anda tidak dapat diterapkan, karena tidak t program yang signifikan dan bermanfaat.
Ben Voigt
@ BenVoigt Saya tidak berpikir saya diharapkan untuk memberikan "program yang signifikan" sebagai jawaban.
user7294900
4

Ini mempromosikan gagasan bahwa kode harus sempurna dan tidak ada bug yang seharusnya ada

Jelas tidak. Ini mempromosikan gagasan bahwa tes Anda tidak boleh gagal, tidak lebih dan tidak kurang. Dengan asumsi bahwa melakukan tes (bahkan banyak dari mereka) mengatakan sesuatu tentang "sempurna" atau "tidak ada bug" adalah kesalahan. Memutuskan seberapa dangkal atau dalam pengujian Anda seharusnya merupakan bagian penting dari penulisan tes yang baik, dan alasan mengapa kami memiliki kategori tes yang terpisah secara terpisah (tes "unit", tes integrasi, "skenario" dalam arti mentimun, dll.).

Disinsentif untuk memikirkan unit test yang akan gagal. Atau tentu saja muncul dengan unit test yang akan sulit untuk diperbaiki.

Dalam pengembangan yang digerakkan oleh tes, adalah wajib bahwa setiap tes unit gagal terlebih dahulu, sebelum mulai kode. Ini disebut "siklus merah-hijau" (atau "siklus merah-hijau-reaktor") karena alasan ini.

  • Tanpa gagal tes, Anda tidak tahu apakah kode tersebut benar-benar diuji oleh tes. Keduanya mungkin tidak berhubungan sama sekali.
  • Dengan mengubah kode untuk persis melakukan pergantian tes dari merah ke hijau, tidak lebih dan tidak kurang, Anda dapat cukup yakin bahwa kode Anda melakukan apa yang seharusnya dilakukan, dan tidak lebih banyak (yang mungkin tidak perlu).

Jika pada suatu saat semua tes unit lulus, maka tidak ada gambaran besar dari keadaan perangkat lunak pada suatu titik waktu. Tidak ada roadmap / tujuan.

Tes lebih merupakan tujuan mikro. Dalam pengembangan test-driven, programmer akan menulis tes (tunggal) terlebih dahulu, dan kemudian memiliki tujuan yang jelas untuk mengimplementasikan beberapa kode; maka tes selanjutnya, dan seterusnya.

Fungsi tes tidak ada di sana dalam kelengkapan sebelum kode ditulis.

Ketika dilakukan dengan benar, dalam bahasa dan dengan pustaka pengujian yang sangat cocok untuk pendekatan ini, ini sebenarnya dapat mempercepat pengembangan secara besar-besaran, karena pesan kesalahan (pengecualian / stacktraces) dapat langsung mengarahkan pengembang ke tempat ia perlu melakukan pekerjaan lanjut.

Ini menghalangi tes unit menulis di muka - sebelum implementasi.

Saya tidak melihat bagaimana pernyataan ini benar. Tes menulis idealnya harus menjadi bagian dari implementasi.

Apakah saya melewatkan sesuatu di sini? Mengapa organisasi mengharapkan semua tes unit lulus?

Karena organisasi mengharapkan tes memiliki relevansi dengan kode. Menulis tes yang berhasil berarti Anda telah mendokumentasikan beberapa bagian dari aplikasi Anda, dan telah membuktikan bahwa aplikasi tersebut melakukan apa yang dikatakan (tes). Tidak lebih dan tidak kurang.

Juga, bagian yang sangat besar dari tes adalah "regresi". Anda ingin dapat mengembangkan atau memperbaiki kode baru dengan percaya diri. Memiliki sejumlah besar tes hijau memungkinkan Anda melakukan itu.

Ini beranjak dari organisasi ke level psikologis. Seorang pengembang yang tahu bahwa kesalahannya kemungkinan besar akan tertangkap oleh tes akan jauh lebih bebas untuk menghasilkan solusi yang cerdas dan berani untuk masalah yang perlu dipecahkan. Di sisi lain, seorang pengembang yang tidak memiliki tes, setelah beberapa waktu, akan terhenti (karena takut) karena dia tidak pernah tahu jika perubahan yang dilakukannya merusak sisa aplikasi.

Bukankah ini hidup di dunia mimpi?

Tidak. Bekerja dengan aplikasi yang digerakkan oleh ujian adalah kegembiraan murni - kecuali jika Anda tidak menyukai konsep untuk alasan apa pun ("lebih banyak usaha" dll.) Yang dapat kita bahas dalam pertanyaan lain.

Dan bukankah itu benar-benar menghalangi pemahaman kode sebenarnya?

Sama sekali tidak, mengapa?

Anda menemukan banyak proyek open source besar (yang manajemen "pemahaman" dan pengetahuan tentang kode adalah topik yang sangat mendesak) yang benar-benar menggunakan tes sebagai dokumentasi utama dari perangkat lunak, dengan, selain sebagai tes, juga memberikan contoh nyata, yang berfungsi, yang secara sintaksis benar untuk pengguna atau pengembang aplikasi / perpustakaan. Ini sering berhasil dengan sangat baik.

Jelas, menulis tes buruk itu buruk. Tapi itu tidak ada hubungannya dengan fungsi tes itu sendiri.

AnoE
sumber
3

(Dari komentar asli saya)

Ada perbedaan antara fungsionalitas yang diperlukan dan tujuan masa depan. Tes untuk fungsionalitas yang diperlukan: mereka tepat, formal, dapat dieksekusi, dan jika gagal, perangkat lunak tidak berfungsi. Sasaran masa depan mungkin tidak tepat atau formal, apalagi dapat dieksekusi, jadi lebih baik dibiarkan dalam bahasa alami seperti di pelacak isu / bug, dokumentasi, komentar, dll.

Sebagai latihan, coba ganti frase "unit test" dalam pertanyaan Anda dengan "kesalahan kompiler" (atau "kesalahan sintaksis", jika tidak ada kompiler). Jelas bahwa suatu rilis seharusnya tidak memiliki kesalahan kompiler, karena itu tidak dapat digunakan; namun kesalahan penyusun dan kesalahan sintaksis adalah keadaan normal pada mesin pengembang saat mereka menulis kode. Kesalahan hanya hilang saat mereka selesai; dan saat itulah kode harus didorong. Sekarang ganti "kesalahan kompiler" dalam paragraf ini dengan "unit test" :)

Warbo
sumber
2

Tujuan dari tes otomatis adalah untuk memberi tahu Anda ketika Anda telah memecahkan sesuatu sedini mungkin . Alur kerjanya terlihat seperti ini:

  1. Membuat perubahan
  2. Bangun dan uji perubahan Anda (idealnya secara otomatis)
  3. Jika tes gagal, itu berarti Anda merusak sesuatu yang sebelumnya berfungsi
  4. jika tes lulus, Anda harus yakin bahwa perubahan Anda tidak menghasilkan regresi baru (tergantung pada cakupan tes)

Jika tes Anda sudah gagal maka langkah # 3 tidak bekerja secara efektif - tes akan gagal, tetapi Anda tidak tahu apakah itu berarti Anda memecahkan sesuatu atau tidak tanpa menyelidiki. Mungkin Anda bisa menghitung jumlah tes gagal, tetapi kemudian perubahan mungkin memperbaiki satu bug dan memecahkan bug lain, atau tes mungkin mulai gagal karena alasan yang berbeda. Ini berarti Anda perlu menunggu beberapa saat sebelum Anda tahu apakah ada sesuatu yang rusak, baik sampai semua masalah telah diperbaiki atau sampai setiap tes gagal diselidiki.

Kemampuan untuk pengujian unit untuk menemukan bug yang baru diperkenalkan sedini mungkin adalah hal yang paling berharga tentang pengujian otomatis - semakin lama kerusakan tidak ditemukan, semakin mahal biaya perbaikannya.

Ini mempromosikan gagasan bahwa kode harus sempurna dan tidak ada bug yang harus ada.
Merupakan disinsentif untuk memikirkan unit test yang akan gagal.

Tes untuk hal-hal yang tidak bekerja tidak mengatakan apa-apa - menulis unit test untuk hal-hal yang dilakukan pekerjaan, atau bahwa Anda akan memperbaikinya. Itu tidak berarti perangkat lunak Anda bebas cacat, itu berarti bahwa tidak ada cacat yang sebelumnya Anda buat untuk unit test yang kembali lagi.

Ini menghalangi tes unit menulis di muka

Jika itu berhasil untuk Anda kemudian tulis tes di depan, tapi jangan periksa ke master / trunk Anda sampai lulus.

Jika pada suatu saat semua tes unit lulus, maka tidak ada gambaran besar dari keadaan perangkat lunak pada suatu titik waktu. Tidak ada roadmap / tujuan.

Tes unit bukan untuk menetapkan peta jalan / sasaran, mungkin menggunakan jaminan simpanan untuk itu? Jika semua tes Anda lulus maka "gambaran besarnya" adalah bahwa perangkat lunak Anda tidak rusak (jika cakupan pengujian Anda baik). Sudah selesai dilakukan dengan baik!

Justin
sumber
2

Jawaban yang ada tentu baik, tetapi saya belum melihat ada yang membahas kesalahpahaman mendasar ini dalam pertanyaan:

pada setiap titik waktu semua unit test harus lulus

Tidak. Paling pasti, ini tidak akan benar. Saat saya sedang mengembangkan perangkat lunak, NCrunch paling sering berwarna cokelat (gagal bangun) atau merah (gagal uji).

Di mana NCrunch harus berwarna hijau (semua tes lulus) adalah ketika saya siap untuk mendorong komit ke server kontrol sumber, karena pada saat itu orang lain mungkin memerlukan ketergantungan pada kode saya.

Ini juga dimasukkan ke dalam topik membuat tes baru: tes harus menegaskan logika dan perilaku kode. Kondisi batas, kondisi kesalahan, dll. Ketika saya menulis tes baru, saya mencoba mengidentifikasi "hot spot" ini dalam kode.

Tes unit mendokumentasikan bagaimana saya mengharapkan kode saya dipanggil - prasyarat, keluaran yang diharapkan, dll.

Jika tes gagal mengikuti perubahan, saya perlu memutuskan apakah kode atau tes tersebut salah.


Sebagai catatan, pengujian unit terkadang berjalan seiring dengan Test Driven Development. Salah satu prinsip TDD adalah bahwa tes yang rusak adalah tiang petunjuk Anda. Ketika tes gagal, Anda harus memperbaiki kode sehingga tes lulus. Ini adalah contoh nyata dari awal minggu ini:

Latar Belakang : Saya menulis dan sekarang mendukung perpustakaan yang digunakan oleh pengembang kami yang digunakan untuk memvalidasi permintaan Oracle. Kami memiliki tes yang menyatakan bahwa kueri cocok dengan beberapa nilai yang diharapkan, yang menjadikan case penting (bukan di Oracle) dan dengan senang hati menyetujui kueri yang tidak valid selama mereka benar-benar cocok dengan nilai yang diharapkan.

Sebagai gantinya, perpustakaan saya mem-parsing kueri menggunakan Antlr dan sintaks Oracle 12c, dan kemudian membungkus berbagai pernyataan pada pohon sintaksis itu sendiri. Hal-hal seperti, itu valid (tidak ada kesalahan parse dinaikkan), semua parameternya dipenuhi oleh koleksi parameter, semua kolom yang diharapkan dibaca oleh pembaca data ada dalam kueri, dll. Semua ini adalah item yang telah diselipkan ke produksi pada berbagai waktu.

Salah satu rekan insinyur saya mengirimi saya pertanyaan pada hari Senin yang gagal (atau lebih tepatnya, berhasil ketika seharusnya gagal) selama akhir pekan. Perpustakaan saya mengatakan sintaksinya baik-baik saja, tetapi meledak ketika server mencoba menjalankannya. Dan ketika dia melihat permintaan, jelas mengapa:

UPDATE my_table(
SET column_1 = 'MyValue'
WHERE id_column = 123;

Saya memuat proyek dan menambahkan tes unit yang menyatakan bahwa permintaan ini seharusnya tidak valid. Jelas, tes gagal.

Berikutnya, saya debugged tes gagal, melangkah melalui kode di mana saya harapkan untuk membuang pengecualian, dan tahu bahwa Antlr itu menaikkan kesalahan pada paren terbuka, hanya saja tidak dengan cara kode sebelumnya mengharapkan. Saya memodifikasi kode, memverifikasi bahwa tes sekarang hijau (lulus) dan tidak ada orang lain yang rusak dalam proses, berkomitmen, dan mendorong.

Ini mungkin memakan waktu 20 menit, dan dalam prosesnya saya benar-benar meningkatkan perpustakaan secara signifikan karena sekarang mendukung seluruh jajaran kesalahan yang sebelumnya telah diabaikan. Jika saya tidak memiliki tes unit untuk perpustakaan, meneliti dan memperbaiki masalah ini bisa memakan waktu berjam-jam.

GalacticCowboy
sumber
0

Satu hal yang menurut saya tidak keluar dari jawaban sebelumnya adalah bahwa ada perbedaan antara tes internal dan tes eksternal (dan saya pikir banyak proyek tidak cukup hati-hati untuk membedakan keduanya). Tes internal menguji bahwa beberapa komponen internal berfungsi sebagaimana mestinya; tes eksternal menunjukkan bahwa sistem secara keseluruhan berfungsi sebagaimana mestinya. Tentu saja sangat mungkin untuk memiliki kegagalan dalam komponen yang tidak mengakibatkan kegagalan sistem (mungkin ada fitur komponen yang tidak digunakan sistem, atau mungkin sistem pulih dari kegagalan sistem). komponen). Kegagalan komponen yang tidak mengakibatkan kegagalan sistem seharusnya tidak menghentikan Anda melepaskannya.

Saya telah melihat proyek yang lumpuh karena terlalu banyak tes komponen internal. Setiap kali Anda mencoba dan menerapkan peningkatan kinerja, Anda memecahkan puluhan tes, karena Anda mengubah perilaku komponen tanpa benar-benar mengubah perilaku sistem yang terlihat secara eksternal. Hal ini menyebabkan kurangnya kelincahan dalam proyek secara keseluruhan. Saya percaya investasi dalam pengujian sistem eksternal umumnya memiliki hasil yang jauh lebih baik daripada investasi dalam pengujian komponen internal, terutama ketika Anda berbicara tentang komponen yang sangat rendah.

Ketika Anda menyarankan bahwa tes unit gagal tidak terlalu penting, saya bertanya-tanya apakah ini yang Anda pikirkan? Mungkin Anda harus menilai nilai tes unit dan membuang yang menyebabkan lebih banyak masalah daripada nilainya, sambil lebih fokus pada tes yang memverifikasi perilaku aplikasi yang terlihat secara eksternal.

Michael Kay
sumber
Saya pikir apa yang Anda gambarkan sebagai "tes eksternal" sering digambarkan di tempat lain sebagai tes "integrasi".
GalacticCowboy
Ya, tapi saya menemukan perbedaan dalam terminologi. Bagi sebagian orang, pengujian integrasi lebih tentang konfigurasi perangkat lunak / perangkat keras / jaringan yang digunakan, sedangkan saya sedang berbicara tentang perilaku eksternal perangkat lunak yang sedang Anda kembangkan.
Michael Kay
0

"tetapi pada setiap titik waktu semua tes unit harus lulus"

Jika itu sikap di perusahaan Anda, itu masalah. Pada waktu TERTENTU, yaitu, ketika kami menyatakan bahwa kode siap untuk pindah ke lingkungan berikutnya, semua tes unit harus lulus. Tetapi selama pengembangan, kita harus secara rutin berharap banyak unit test gagal.

Tidak ada orang yang beralasan mengharapkan seorang programmer untuk mendapatkan pekerjaannya sempurna pada percobaan pertama. Apa yang kami harapkan secara wajar adalah bahwa ia akan terus mengerjakannya sampai tidak ada masalah yang diketahui.

"Adalah disinsentif untuk memikirkan unit test yang akan gagal. Atau tentu saja muncul dengan unit test yang akan sulit untuk diperbaiki." Jika seseorang di organisasi Anda berpikir bahwa mereka tidak boleh menyebutkan tes yang mungkin karena mungkin gagal dan menyebabkan mereka lebih banyak pekerjaan untuk memperbaikinya, orang itu benar-benar tidak memenuhi syarat untuk pekerjaan mereka. Ini adalah sikap yang membawa malapetaka. Apakah Anda ingin dokter yang mengatakan, "Ketika saya melakukan operasi, saya sengaja tidak memeriksa apakah jahitannya benar, karena jika saya melihat mereka tidak, saya harus kembali dan melakukannya lagi dan itu akan memperlambat penyelesaian operasi "?

Jika tim memusuhi programmer yang mengidentifikasi kesalahan sebelum kode diproduksi, Anda memiliki masalah nyata dengan sikap tim itu. Jika manajemen menghukum programmer yang mengidentifikasi kesalahan yang memperlambat pengiriman, kemungkinan perusahaan Anda menuju kebangkrutan.

Ya, memang benar bahwa kadang-kadang orang yang rasional mengatakan, "Kami mendekati tenggat waktu, ini masalah sepele dan tidak layak mencurahkan sumber daya sekarang karena diperlukan untuk memperbaikinya." Tetapi Anda tidak dapat membuat keputusan itu secara rasional jika Anda tidak tahu. Dengan dingin memeriksa daftar kesalahan dan menetapkan prioritas dan jadwal untuk memperbaikinya adalah rasional. Sengaja membuat diri Anda tidak mengetahui masalah sehingga Anda tidak harus membuat keputusan ini bodoh. Apakah Anda pikir pelanggan tidak akan mengetahuinya hanya karena Anda tidak ingin tahu?

Jay
sumber
-7

Ini adalah contoh spesifik bias konfirmasi , di mana orang cenderung mencari informasi yang menegaskan keyakinan mereka yang ada.

Salah satu contoh terkenal dari ini terjadi, adalah dalam permainan 2,4,6.

  • Saya memiliki aturan di kepala saya bahwa serangkaian tiga angka akan berlalu atau gagal,
  • 2,4,6 adalah pass
  • Anda dapat mendaftar set tiga angka, dan saya akan memberitahu Anda jika mereka lulus atau gagal.

Kebanyakan orang memilih aturan, katakan "kesenjangan antara angka 1 dan 2 sama dengan perbedaan antara angka 2 dan 3."

Mereka akan menguji beberapa angka:

  • 4, 8, 12? Lulus
  • 20, 40, 60? Lulus
  • 2, 1004, 2006? Lulus

Mereka berkata, "Ya, setiap pengamatan menegaskan hipotesis saya, itu pasti benar." Dan umumkan aturan mereka kepada orang yang memberikan teka-teki.

Tetapi mereka tidak pernah menerima satu pun 'gagal' untuk set tiga angka. Aturannya bisa saja 'tiga angka perlu angka' untuk semua informasi yang mereka miliki.

Aturannya sebenarnya hanya bahwa angkanya dalam urutan menaik. Orang-orang biasanya hanya mendapatkan teka-teki ini dengan benar jika mereka menguji kegagalan. Kebanyakan orang salah, dengan memilih aturan yang lebih spesifik, dan hanya menguji angka yang memenuhi aturan khusus ini.

Mengenai mengapa orang-orang jatuh ke bias konfirmasi, dan mungkin melihat unit test gagal sebagai bukti masalah, ada banyak psikolog yang dapat menjelaskan bias konfirmasi lebih baik daripada saya, pada dasarnya turun ke orang-orang yang tidak menyukai kesalahan, dan berjuang untuk benar-benar berusaha untuk membuktikan diri mereka salah.

Scott
sumber
2
Bagaimana itu relevan dengan pertanyaan? Tes unit gagal adalah bukti masalah, menurut definisi.
Frax
1
Anda benar - benar dapat memiliki unit test yang mengharuskan sistem yang diuji memasuki mode kegagalan. Itu tidak sama dengan tidak pernah melihat tes gagal. Itu juga mengapa TDD ditentukan sebagai siklus "Merah-> Hijau-> Refactor"
Caleth