Apakah ada kebutuhan untuk melakukan tes untuk fungsi sederhana (mandiri)?

36

Pertimbangkan ini:

public function polynominal($a, $b, $c, $d)
{
    return  $a * pow($x, 3) + $b * pow($x, 2) + $c * $x + $d;
}

Misalkan Anda menulis berbagai tes untuk fungsi di atas dan membuktikan kepada diri sendiri dan orang lain bahwa "itu bekerja".

Mengapa tidak menghapus tes itu, dan hidup bahagia selamanya? Poin saya adalah bahwa beberapa fungsi tidak perlu diuji terus menerus setelah mereka terbukti bekerja. Saya mencari titik tandingan yang menyatakan, ya fungsi-fungsi ini masih perlu diuji, karena: ... atau ya, ini tidak perlu diuji ...

Dennis
sumber
32
Bukankah itu benar untuk setiap fungsi yang kecuali Anda mengubah kode Anda, jika bekerja kemarin, itu akan bekerja besok juga? Intinya adalah bahwa perangkat lunak yang berubah.
5gon12eder
3
Karena tidak ada yang akan menulis override_function('pow', '$a,$b', 'return $a * $b;');dalam pikiran mereka ... atau mencoba menulis ulang untuk menangani angka yang kompleks.
8
Jadi ... um ... bahwa "terbukti menjadi kode bebas bug" ... ia memiliki bug .
Untuk fungsi kecil seperti ini, Anda mungkin ingin mempertimbangkan pengujian berbasis properti. Pengujian berbasis properti secara otomatis menghasilkan testcases, dan tes untuk invarian yang ditentukan sebelumnya. Mereka menyediakan dokumentasi melalui invarian, yang membuatnya berguna untuk disimpan. Untuk fungsi sederhana seperti ini, mereka sempurna.
Martijn
6
Juga fungsi itu adalah kandidat yang bagus untuk perubahan ke bentuk Horner (($a*$x + $b)*$x + $c)*$x + $dyang mudah salah, tetapi seringkali jauh lebih cepat. Hanya karena Anda pikir itu tidak akan berubah tidak berarti itu tidak akan terjadi.
Michael Anderson

Jawaban:

78

Pengujian regresi

Ini semua tentang pengujian regresi .

Bayangkan pengembang berikutnya melihat metode Anda dan memperhatikan bahwa Anda menggunakan angka ajaib. Dia diberi tahu bahwa angka ajaib itu jahat, jadi dia menciptakan dua konstanta, satu untuk nomor dua, satu lagi untuk nomor tiga — tidak ada yang salah dalam melakukan perubahan ini; itu tidak seperti dia memodifikasi implementasi Anda yang sudah benar.

Karena terganggu, ia membalikkan dua konstanta.

Dia melakukan kode, dan semuanya tampak berfungsi dengan baik, karena tidak ada pengujian regresi yang berjalan setelah setiap komit.

Suatu hari (bisa berminggu-minggu kemudian), sesuatu pecah di tempat lain. Dan di tempat lain, maksud saya di lokasi yang sepenuhnya berlawanan dari basis kode, yang tampaknya tidak ada hubungannya dengan polynominalfungsi. Jam debugging menyakitkan mengarah ke pelakunya. Selama ini, aplikasi terus gagal dalam produksi, menyebabkan banyak masalah kepada pelanggan Anda.

Menjaga tes asli yang Anda tulis dapat mencegah rasa sakit seperti itu. Pengembang yang terganggu akan melakukan kode, dan segera melihat bahwa dia memecahkan sesuatu; kode seperti itu bahkan tidak akan mencapai produksi. Tes unit juga akan sangat tepat tentang lokasi kesalahan . Memecahkannya tidak akan sulit.

Efek samping ...

Sebenarnya, sebagian besar refactoring sangat didasarkan pada pengujian regresi. Buat perubahan kecil. Uji. Jika lewat, semuanya baik-baik saja.

Efek sampingnya adalah jika Anda tidak memiliki tes, maka praktis setiap refactoring menjadi risiko besar melanggar kode. Mengingat ada banyak kasus, sudah sulit untuk menjelaskan kepada manajemen bahwa refactoring harus dilakukan, akan lebih sulit untuk melakukannya setelah upaya refactoring Anda sebelumnya memperkenalkan banyak bug.

Dengan memiliki serangkaian tes lengkap, Anda mendorong refactoring, dan kode yang lebih baik dan lebih bersih. Bebas risiko, sangat menggoda untuk melakukan refactor lebih banyak, secara teratur.

Perubahan persyaratan

Aspek penting lainnya adalah bahwa persyaratan berubah. Anda mungkin diminta untuk menangani angka yang rumit , dan tiba-tiba, Anda perlu mencari log kontrol versi untuk menemukan tes sebelumnya, mengembalikannya, dan mulai menambahkan tes baru.

Kenapa semua ini merepotkan? Mengapa menghapus tes untuk menambahkannya nanti? Anda bisa menyimpannya di tempat pertama.

Arseni Mourzenko
sumber
Catatan pedantic: tidak ada yang bebas risiko. ;)
jpmc26
2
Regresi adalah alasan nomor satu untuk tes bagi saya - bahkan jika kode Anda jelas tentang tanggung jawab dan hanya menggunakan apa pun yang diberikan padanya (misalnya tidak ada global), itu semua terlalu mudah untuk memecahkan sesuatu secara tidak sengaja. Tes tidak sempurna, tetapi masih bagus (dan hampir selalu mencegah bug yang sama muncul lagi, sesuatu yang sebagian besar pelanggan tidak senang). Jika tes Anda berhasil, tidak ada biaya perawatan. Jika mereka berhenti bekerja, Anda mungkin menemukan bug. Saya sedang mengerjakan aplikasi warisan dengan ribuan global - tanpa tes, saya tidak akan berani melakukan banyak perubahan yang diperlukan.
Luaan
Catatan pedantic lain: Beberapa angka "ajaib" sebenarnya ok, dan menggantinya dengan konstanta adalah ide yang buruk.
Deduplicator
3
@Dupuplikator kita tahu itu, tetapi banyak programmer junior yang baru dilatih di universitas sangat bersemangat tentang mantra mengikuti secara membabi buta. Dan "angka ajaib itu jahat" adalah salah satunya. Yang lain adalah "apa pun yang digunakan lebih dari satu kali harus di refactored menjadi sebuah metode" yang mengarah pada kasus ekstrem ke sejumlah besar metode yang hanya berisi satu pernyataan (dan kemudian mereka bertanya-tanya mengapa kinerja tiba-tiba turun, mereka tidak pernah belajar tentang callstacks).
jwenting
@jwenting: Baru saja menambahkan catatan itu karena MainMa menulis "tidak ada yang salah dalam melakukan perubahan ini".
Deduplicator
45

Karena tidak ada yang begitu sederhana sehingga tidak ada bug.

Kode Anda, sementara itu tampaknya bebas bug. Ini sebenarnya adalah representasi programatik sederhana dari fungsi polinomial.

Kecuali itu memiliki bug ...

public function polynominal($a, $b, $c, $d)
{
    return  $a * pow($x, 3) + $b * pow($x, 2) + $c * $x + $d;
}

$xtidak didefinisikan sebagai input ke kode Anda, dan tergantung pada bahasa atau runtime, atau pelingkupan, fungsi Anda mungkin tidak berfungsi, dapat menyebabkan hasil yang tidak valid, atau mungkin menabrak pesawat ruang angkasa .


Tambahan:

Meskipun Anda dapat mempertimbangkan bug kode Anda gratis untuk saat ini, berapa lama hal ini tetap sulit untuk dikatakan. Meskipun dapat diperdebatkan bahwa menulis tes untuk sepotong kode sepele seperti itu tidak layak, setelah menulis tes itu pekerjaan dilakukan dan menghapusnya menghapus penjaga yang nyata.

Catatan tambahan adalah layanan cakupan kode (seperti coveralls.io) yang memberikan indikasi yang baik tentang cakupan yang disediakan oleh test suite. Dengan mencakup setiap baris kode, Anda memberikan metrik jumlah yang layak (jika bukan kualitas) pengujian yang Anda lakukan. Dalam kombinasi dengan banyak tes kecil, layanan ini setidaknya memberi tahu Anda di mana tidak mencari bug ketika itu terjadi.

Pada akhirnya, jika Anda sudah memiliki tes tertulis, pertahankan . Karena ruang atau waktu yang dihemat dari penghapusan, kemungkinan akan jauh lebih sedikit daripada hutang teknis nanti jika bug muncul.


sumber
Ada solusi alternatif: beralih ke bahasa non-dinamis non-lemah. Tes unit biasanya merupakan solusi untuk verifikasi waktu kompilasi yang kurang kuat dan beberapa bahasa cukup bagus di en.wikipedia.org/wiki/Agda_(programming_language)
Den
21

Iya nih. Jika kita dapat mengatakan dengan keyakinan 100%, dengan pasti: fungsi ini tidak akan pernah diedit dan tidak akan pernah berjalan dalam konteks yang dapat menyebabkannya gagal - jika kita bisa mengatakan itu, kita bisa membatalkan tes dan menyimpan beberapa milidetik pada setiap CI membangun.

Tetapi kita tidak bisa. Atau, kita tidak bisa dengan banyak fungsi. Dan lebih mudah untuk memiliki aturan menjalankan semua tes sepanjang waktu daripada berupaya menentukan secara pasti ambang batas keyakinan apa yang kami puasi, dan persis berapa banyak kepercayaan yang kami miliki dalam ketidakberdayaan dan kemaksuman fungsi apa pun yang diberikan.

Dan waktu pemrosesan murah. Milidetik yang disimpan, bahkan berkali-kali lipat, tidak bertambah hingga cukup untuk membenarkan waktu dengan setiap fungsi untuk bertanya: apakah kita memiliki keyakinan yang cukup bahwa kita tidak perlu mengujinya lagi?

Carl Manaster
sumber
Saya pikir itu poin yang sangat baik yang Anda bawa ke sini. Saya sudah bisa melihat dua orang menghabiskan berjam-jam berdebat tentang menjaga tes untuk beberapa fungsi kecil.
Kapol
@Kapol: bukan argumen, pertemuan untuk menentukan mana yang bisa dan mana yang bisa tinggal, dan kriteria apa yang harus digunakan untuk memutuskan, serta ke mana mendokumentasikan kriteria, dan siapa yang menandatangani keputusan akhir ...
jmoreno
12

Semua yang dikatakan dalam jawaban lain benar, tetapi saya akan menambahkan satu lagi.

Dokumentasi

Tes unit, jika ditulis dengan baik, dapat menjelaskan kepada pengembang apa fungsi sebenarnya, apa harapan input / outputnya, dan yang lebih penting, perilaku apa yang bisa diharapkan darinya.

Ini dapat membuat bercak bug lebih mudah dan mengurangi kebingungan.

Tidak semua orang ingat polinomial, geometri, atau bahkan aljabar :) Tapi tes unit yang bagus untuk kontrol versi akan mengingat untuk saya.

Untuk contoh betapa bermanfaatnya itu sebagai dokumentasi, lihat pengantar Jasmine: http://jasmine.github.io/edge/introduction.html Beri waktu beberapa detik untuk memuat, lalu gulir ke bawah. Anda akan melihat seluruh Jasmine API didokumentasikan sebagai output uji unit.

[Pembaruan berdasarkan umpan balik dari @Warbo] Tes dijamin mutakhir juga karena jika tidak, mereka akan gagal, yang umumnya akan menyebabkan kegagalan build jika CI digunakan. Dokumentasi eksternal berubah secara independen dari kode, dan karena itu tidak selalu mutakhir.

Brandon
sumber
1
Ada keuntungan besar untuk menggunakan tes sebagai (satu bagian dari) dokumentasi yang Anda tinggalkan secara implisit: mereka diperiksa secara otomatis. Bentuk dokumentasi lain, misalnya. komentar penjelas atau cuplikan kode pada halaman Web, dapat keluar dari tanggal saat kode berevolusi. Tes unit akan memicu kegagalan jika tidak lagi akurat. Halaman melati itu adalah contoh ekstrem dari ini.
Warbo
Saya ingin menambahkan bahwa beberapa bahasa, seperti D dan Rust, mengintegrasikan generasi dokumentasi mereka dengan pengujian unit mereka, sehingga Anda dapat memiliki bagian kode yang sama dikompilasi ke dalam unit test dan dimasukkan ke dalam dokumentasi HTML
Idan Arye
2

Pengecekan kenyataan

Saya telah berada di lingkungan yang menantang di mana pengujian adalah "buang-buang waktu" selama penganggaran dan jadwal, dan kemudian "bagian mendasar dari jaminan kualitas" setelah pelanggan berurusan dengan bug, jadi pendapat saya lebih fleksibel daripada yang lain.

Anda punya anggaran. Tugas Anda adalah untuk mendapatkan produk terbaik sesuai anggaran Anda, untuk definisi "terbaik" apa pun yang dapat Anda gesek bersama (itu bukan kata yang mudah untuk didefinisikan). Akhir dari cerita.

Pengujian adalah suatu alat dalam inventaris Anda. Anda harus menggunakannya, karena ini adalah alat yang bagus , dengan sejarah panjang dalam menghemat jutaan, atau bahkan miliaran dolar. Jika diberi kesempatan, Anda harus menambahkan tes ke fungsi-fungsi sederhana ini. Ini mungkin menyelamatkan kulit Anda suatu hari nanti.

Tetapi di dunia nyata, dengan keterbatasan anggaran dan jadwal, itu mungkin tidak terjadi. Jangan menjadikan diri Anda budak dari prosedur. Fungsi pengujian memang bagus, tetapi pada titik tertentu, jam kerja Anda mungkin lebih baik digunakan untuk menulis dokumentasi pengembang dalam kata-kata, daripada kode, sehingga pengembang berikutnya tidak perlu banyak pengujian. Atau mungkin lebih baik dihabiskan refactoring basis kode sehingga Anda tidak harus mempertahankan sesulit binatang buas. Atau mungkin waktu itu lebih baik dihabiskan untuk berbicara dengan bos Anda tentang anggaran dan jadwal sehingga dia lebih memahami apa yang mereka menawar ketika putaran pendanaan berikutnya berakhir.

Pengembangan perangkat lunak adalah keseimbangan. Selalu hitung biaya peluang dari apa pun yang Anda lakukan untuk memastikan tidak ada cara yang lebih baik untuk menghabiskan waktu Anda.

Cort Ammon
sumber
4
Dalam hal ini, sudah ada tes, jadi pertanyaannya bukan apakah harus menulisnya, tetapi apakah akan melakukannya dan menjalankannya dengan setiap build atau rilis atau jadwal apa pun yang ada untuk menjalankan semua tes.
Paŭlo Ebermann
0

Ya, teruskan tesnya, terus jalankan dan teruskan.

Tes unit ada untuk melindungi Anda (dan orang lain) dari diri Anda (dan diri mereka sendiri).

Mengapa menyimpan tes itu ide yang bagus;

  • Validasi fungsionalitas persyaratan sebelumnya dalam menghadapi persyaratan baru dan fungsionalitas tambahan
  • Pastikan latihan refactoring benar
  • Dokumentasi internal - ini adalah bagaimana kode diharapkan untuk digunakan
  • Pengujian regresi, semuanya berubah
    • Apakah perubahan melanggar kode lama?
    • Apakah perubahan memerlukan permintaan perubahan lebih lanjut atau pembaruan untuk fungsi atau kode saat ini?
  • Mengingat bahwa tes sudah ditulis, pertahankan; ini adalah waktu dan uang yang sudah dihabiskan yang akan mengurangi biaya perawatan lebih lanjut
Niall
sumber
2
Tampaknya ini tidak menawarkan sesuatu yang substansial atas jawaban lain yang telah disediakan.
1
Ada masalah saat mempostingnya sebelumnya, tetapi jika dipikir-pikir, ini adalah ringkasan yang bagus. Saya akan menghapusnya.
Niall
-1

Dokumentasi Pengembang

  • Bagaimana saya (sebagai pengembang lain) tahu bahwa ini telah diuji?
  • Jika saya ingin memperbaiki bug di fungsi mandiri , bagaimana saya tahu bahwa saya tidak memperkenalkan bug yang sudah Anda pertimbangkan?
  • Indikator kompleksitas: # tes dapat menjadi ukuran yang baik tentang seberapa kompleks sesuatu itu. Ini mungkin menunjukkan bahwa Anda tidak boleh menyentuhnya karena sudah matang dan stabil atau kembung dan berpasangan.

Dokumentasi Pengguna

  • Sambil menunggu dokumen yang buruk, saya dapat melihat dan melihat apakah {nol, nilai negatif, set kosong, dll} diterima dan berapa nilai pengembalian yang diharapkan.
  • Ini juga memberikan contoh yang baik tentang bagaimana saya harus menggunakan objek / fungsi
sixtyfootersdude
sumber
1
ini tampaknya tidak menawarkan sesuatu yang substansial atas poin yang dibuat dan dijelaskan dalam jawaban sebelumnya. Bahkan "ringkasan yang bagus" sudah diposting (menurut selera saya, itu tidak terlalu bagus tapi oh well)
agas