Apakah terlalu banyak kode yang berbau?

19

Saya benar-benar jatuh cinta dengan pengujian unit dan TDD - Saya tes terinfeksi.

Namun, pengujian unit biasanya digunakan untuk metode publik. Kadang-kadang meskipun saya harus menguji beberapa asumsi-pernyataan dalam metode pribadi juga, karena beberapa dari mereka "berbahaya" dan refactoring tidak dapat membantu lebih lanjut. (Saya tahu, kerangka pengujian memungkinkan pengujian metode pribadi).

Jadi sudah menjadi kebiasaan saya bahwa baris pertama dan terakhir dari metode pribadi adalah pernyataan.

Namun, saya perhatikan bahwa saya cenderung menggunakan pernyataan dalam metode publik (dan juga pribadi) hanya "untuk memastikan". Mungkinkah ini "pengujian duplikasi" karena asumsi metode publik diuji dari luar oleh kerangka pengujian unit?

Bisakah seseorang menganggap terlalu banyak pernyataan sebagai bau kode?

Florents Tselai
sumber
1
apakah pernyataan ini hidup atau mati dalam rilis?
jk.
Saya bekerja terutama dengan Java di mana pernyataan harus diaktifkan secara manual.
Florents Tselai
Baru saja menemukan ini "Asserts meningkatkan produktivitas dengan melacak bug" programmers.stackexchange.com/a/16317/32342
Florents Tselai
bagaimana Anda mendefinisikan "terlalu banyak"?
haylem
@haylem Ada pernyataan "terlalu banyak" jika programmer lain membaca kode dan mengatakan "Saya bosan dengan pernyataan ini".
Florents Tselai

Jawaban:

26

Pernyataan itu sangat berguna untuk menguji asumsi Anda, tetapi mereka juga melayani tujuan lain yang sangat penting: dokumentasi. Setiap pembaca dari metode publik dapat membaca pernyataan untuk dengan cepat menentukan kondisi sebelum dan sesudah, tanpa harus melihat test suite. Untuk alasan ini, saya sarankan Anda menyimpan pernyataan itu untuk alasan dokumentasi, daripada alasan pengujian. Secara teknis Anda menduplikasi pernyataan, tetapi mereka melayani dua tujuan yang berbeda dan sangat berguna dalam keduanya.

Menjaga mereka sebagai pernyataan lebih baik daripada hanya menggunakan komentar, karena mereka secara aktif memeriksa asumsi setiap kali dijalankan.

Oleksi
sumber
2
Poin yang sangat bagus!
Florents Tselai
1
Pernyataan adalah dokumentasi, tetapi itu tidak membuat duplikasi itu sah. Sebaliknya, bahkan menimbulkan kecurigaan tentang kualitas tes jika tampaknya pernyataan yang sama diperlukan lebih dari sekali.
Yam Marcovic
1
@YamMarcovic Saya pikir ini dapat diterima karena dua potongan kode "digandakan" melayani dua tujuan yang berbeda dan berbeda. Saya pikir sangat berguna untuk memilikinya di kedua lokasi, meskipun ada duplikasi. Memiliki asersi dalam metode ini sangat berharga untuk dokumentasi, dan jelas diperlukan untuk pengujian.
Oleksi
2
Pernyataan dalam kode untuk saya melengkapi pernyataan pengujian unit. Anda tidak akan memiliki cakupan pengujian 100% dan pernyataan dalam kode akan membantu Anda mempersempit kegagalan unit test lebih cepat.
sebastiangeiger
15

Sepertinya Anda mencoba melakukan Desain-dengan-Kontrak dengan tangan.

Melakukan DbC adalah ide yang bagus, tetapi Anda setidaknya harus mempertimbangkan beralih ke bahasa yang mendukungnya secara native (seperti Eiffel ) atau setidaknya menggunakan kerangka kontrak untuk platform Anda (misalnya Kontrak Kode Microsoft untuk .NETcukup bagus, bisa dibilang kerangka kontrak paling canggih di luar sana, bahkan lebih kuat dari Eiffel). Dengan begitu, Anda dapat lebih memanfaatkan kekuatan kontrak. Misalnya menggunakan kerangka kerja yang ada, Anda dapat memunculkan kontrak dalam dokumentasi Anda atau IDE (mis. Kontrak Kode untuk .NET ditunjukkan dalam IntelliSense dan, jika Anda memiliki VS Ultimate, bahkan dapat diperiksa secara statis oleh teorema teorema otomatis pada waktu kompilasi sementara Anda mengetik; demikian juga banyak kerangka kontrak Java memiliki doclet JavaDoc yang secara otomatis akan mengekstrak kontrak ke dalam dokumentasi JavaDoc API Anda).

Dan bahkan jika ternyata dalam situasi Anda tidak ada alternatif untuk melakukannya secara manual, Anda sekarang setidaknya tahu apa namanya, dan dapat membacanya.

Jadi, singkatnya: jika Anda memang melakukan DbC, bahkan jika Anda tidak mengetahuinya, maka pernyataan itu baik-baik saja.

Jörg W Mittag
sumber
4

Setiap kali Anda tidak memiliki kendali penuh atas parameter input Anda, itu ide yang sangat bagus untuk menguji di muka untuk kesalahan sederhana. Gagal pada nol misalnya.

Ini bukan duplikasi tes Anda, karena mereka harus menguji bahwa kode gagal tepat diberikan parameter input yang buruk, dan kemudian mendokumentasikan bahwa .

Saya tidak berpikir Anda harus menegaskan pada parameter kembali (kecuali jika Anda secara eksplisit memiliki invarian Anda ingin pembaca mengerti). Ini juga merupakan tugas para unittests.

Secara pribadi saya tidak suka assertpernyataan di Jawa, karena mereka dapat dimatikan dan kemudian itu adalah keamanan palsu.


sumber
1
+1 Untuk "Saya tidak suka pernyataan tegas di Jawa, karena mereka dapat dimatikan dan kemudian itu adalah keamanan palsu."
Florents Tselai
3

Saya pikir menggunakan asersi dalam metode publik bahkan lebih penting, karena di sana Anda tidak mengontrol input dan mungkin lebih besar kemungkinan asumsi rusak.

Pengujian kondisi input harus dilakukan di semua metode publik dan yang dilindungi. Jika input diteruskan langsung ke metode pribadi, maka pengujian inputnya mungkin berlebihan.

Menguji kondisi keluaran (misalnya keadaan objek atau nilai pengembalian! = Null) harus dilakukan dalam metode dalam (mis. Metode pribadi). Jika output dilewatkan langsung dari metode pribadi ke output dari metode publik tanpa perhitungan tambahan atau perubahan keadaan dalam, maka pengujian kondisi output dari metode publik mungkin berlebihan.

Saya setuju dengan Oleksi, bagaimanapun, bahwa redundansi dapat meningkatkan keterbacaan dan juga dapat meningkatkan pemeliharaan (jika penugasan langsung atau pengembalian tidak lagi terjadi di masa depan).

Danny Varod
sumber
4
Jika kondisi kesalahan atau kondisi argumen yang tidak valid dapat disebabkan oleh pengguna (pemanggil) dari antarmuka publik, ini harus diberikan perlakuan penanganan kesalahan penuh yang tepat. Ini berarti memformat pesan kesalahan yang bermakna bagi pengguna akhir, disertai dengan kode kesalahan, masuk, dan upaya untuk memulihkan data atau mengembalikan ke keadaan waras. Mengganti penanganan kesalahan yang tepat dengan pernyataan akan membuat kode kurang kuat dan sulit untuk dipecahkan.
rwong
Penegasan dapat (dan dalam banyak kasus seharusnya) mencakup penebangan dan melemparkan pengecualian (atau kode kesalahan dalam C--).
Danny Varod
3

Sulit untuk menjadi agnostik bahasa tentang masalah ini, karena rincian tentang bagaimana penegasan dan penanganan kesalahan / pengecualian yang tepat diterapkan memiliki pengaruh pada jawabannya. Inilah $ 0,02 saya, berdasarkan pengetahuan saya tentang Java & C ++.

Pernyataan dalam metode pribadi adalah hal yang baik, dengan asumsi Anda tidak berlebihan dan menempatkannya di mana - mana . Jika Anda menempatkan mereka pada metode yang sangat sederhana atau berulang kali memeriksa hal-hal seperti bidang yang tidak dapat diubah, maka Anda mengacaukan kodenya dengan sia-sia.

Pernyataan dalam metode publik umumnya sebaiknya dihindari. Anda masih harus melakukan hal-hal seperti memeriksa bahwa kontrak metode tidak dilanggar, tetapi jika demikian maka Anda harus melempar pengecualian yang diketik dengan pesan yang bermakna, mengembalikan keadaan jika memungkinkan, dll. (Apa yang @rwong sebut "penuh" penanganan penanganan kesalahan yang tepat ").

Secara umum, Anda hanya harus menggunakan pernyataan untuk membantu Anda pembangunan / debugging. Anda tidak dapat berasumsi bahwa siapa pun yang menggunakan API Anda bahkan akan memiliki pernyataan diaktifkan ketika mereka menggunakan kode Anda. Meskipun mereka memiliki beberapa kegunaan dalam membantu mendokumentasikan kode, sering kali ada cara yang lebih baik untuk mendokumentasikan hal-hal yang sama (yaitu dokumentasi metode, pengecualian).

vaughandroid
sumber
2

Menambahkan ke daftar (kebanyakan) jawaban luar biasa, alasan lain untuk banyak menegaskan, dan duplikasi, adalah bahwa Anda tidak tahu bagaimana, kapan atau oleh siapa kelas akan dimodifikasi selama waktu hidup itu. Jika Anda menulis kode membuang yang akan mati dalam setahun, bukan masalah. Jika Anda menulis kode yang akan digunakan dalam 20 tahun ke depan, kode itu akan terlihat sangat berbeda - dan asumsi yang Anda buat mungkin tidak lagi valid. Orang yang melakukan perubahan itu akan berterima kasih atas pernyataannya.

Juga tidak semua programmer sempurna - sekali lagi, pernyataan itu berarti bahwa salah satu dari "slip up" itu tidak akan menyebar terlalu jauh.

Jangan meremehkan efek yang ditimbulkan oleh pernyataan ini (dan pemeriksaan lain tentang asumsi) dalam mengurangi biaya perawatan.

mattnz
sumber
0

Memiliki duplikat pernyataan tidak salah, tetapi memiliki pernyataan "hanya untuk memastikan" bukan yang terbaik. Itu benar-benar bermuara pada apa yang sebenarnya setiap tes coba capai. Hanya menegaskan apa yang benar-benar dibutuhkan. Jika tes menggunakan Moq untuk memverifikasi metode dipanggil, tidak masalah apa yang terjadi setelah metode dipanggil, tes hanya berkaitan dengan memastikan metode dipanggil.

Jika setiap tes unit tunggal memiliki set penegasan yang sama, kecuali untuk satu atau dua, maka ketika Anda sedikit refactor semua tes bisa gagal karena alasan yang sama persis, daripada menunjukkan kepada Anda dampak sebenarnya dari refactor. Anda bisa berakhir dalam situasi di mana Anda menjalankan semua tes, semuanya gagal karena setiap tes memiliki menegaskan yang sama, Anda memperbaiki kegagalan itu, menjalankan tes lagi, mereka gagal karena alasan lain, Anda memperbaiki kegagalan itu. Ulangi sampai selesai.

Juga pertimbangkan pemeliharaan proyek pengujian Anda. Apa yang terjadi ketika Anda menambahkan parameter baru, atau outputnya sedikit diubah, apakah Anda harus kembali melalui banyak tes dan mengubah pernyataan atau menambahkan pernyataan? Dan, tergantung pada kode Anda, Anda akhirnya harus mengatur lebih banyak hanya untuk memastikan semua pernyataan lulus.

Saya dapat memahami daya tarik ingin memasukkan duplikat pernyataan dalam unit test, tetapi itu benar-benar berlebihan. Saya menempuh jalur yang sama dengan proyek pengujian pertama saya. Saya menjauh dari itu karena pemeliharaan sendiri membuat saya gila.

bwalk2895
sumber
0

Namun, pengujian unit digunakan untuk metode publik.

Jika kerangka kerja pengujian Anda memungkinkan pengakses, maka unit testing metode pribadi juga merupakan ide bagus (IMO).

Bisakah seseorang menganggap terlalu banyak pernyataan sebagai bau kode?

Saya lakukan. Pernyataan tidak apa-apa, tetapi tujuan pertama Anda harus membuatnya sehingga skenario itu bahkan tidak mungkin ; bukan hanya itu tidak terjadi.

Telastyn
sumber
-2

Ini latihan yang buruk.

Anda tidak boleh menguji metode publik / pribadi. Anda harus menguji kelas secara keseluruhan. Pastikan Anda memiliki cakupan yang baik.

Jika Anda menggunakan cara (primitif) untuk menguji setiap metode secara terpisah sebagai patokan, Anda akan merasa sangat sulit untuk memperbaiki kelas Anda di masa depan. Dan itulah salah satu hal terbaik yang dapat Anda lakukan TDD.

Selain itu, ini adalah duplikasi. Lebih jauh, itu menunjukkan bahwa penulis kode tidak benar-benar tahu apa yang dia lakukan. Dan lucunya, Anda lakukan .

Beberapa orang memperlakukan tes mereka dengan sedikit perhatian dibandingkan kode produksi mereka. Seharusnya tidak. Jika ada, pengalaman saya menyarankan untuk bahkan merawat tes dengan lebih hati-hati. Mereka layak.

Yam Marcovic
sumber
-1 Unit menguji suatu kelas secara keseluruhan bisa berbahaya karena Anda akhirnya menguji lebih dari satu hal per tes. Ini membuat tes sangat rapuh. Anda harus menguji satu perilaku sekaligus, yang seringkali berhubungan dengan pengujian hanya satu metode per tes.
Oleksi
Juga mengenai "itu menunjukkan bahwa penulis kode tidak benar-benar tahu apa yang dia lakukan [...] Anda lakukan.". Pengembang masa depan mungkin tidak sejelas apa yang dilakukan metode tertentu. Dalam hal ini, memiliki kondisi sebelum dan sesudah dalam bentuk penegasan dapat sangat berharga dalam memahami sepotong kode.
Oleksi
2
@Oleksi Pengujian yang baik adalah tentang skenario penggunaan yang valid, bukan tentang menyatakan setiap detail terkecil yang tidak relevan. Redundant menegaskan memang bau kode karena maksud yang tidak jelas, dan hanyalah contoh buruk dari pemrograman defensif.
Yam Marcovic