Tes Penulisan untuk Kode yang Ada

68

Misalkan seseorang memiliki program yang relatif besar (katakanlah 900k SLOC dalam C #), semua berkomentar / didokumentasikan secara menyeluruh, terorganisir dengan baik dan bekerja dengan baik. Seluruh basis kode ditulis oleh pengembang senior tunggal yang tidak lagi bersama perusahaan. Semua kode dapat diuji apa adanya dan IoC digunakan di seluruh - kecuali untuk beberapa alasan aneh mereka tidak menulis tes unit apa pun. Sekarang, perusahaan Anda ingin melakukan percabangan kode dan ingin pengujian unit ditambahkan untuk mendeteksi ketika perubahan merusak fungsionalitas inti.

  • Apakah menambahkan tes merupakan ide yang bagus?
  • Jika demikian, bagaimana seseorang memulai sesuatu seperti ini?

SUNTING

OK, jadi saya tidak mengharapkan jawaban membuat argumen yang bagus untuk kesimpulan yang berlawanan. Masalahnya mungkin keluar dari tangan saya. Saya sudah membaca "pertanyaan duplikat" juga dan konsensus umum adalah bahwa "tes menulis itu baik" ... ya, tapi tidak terlalu membantu dalam kasus khusus ini.

Saya tidak berpikir saya sendirian di sini dalam merenungkan tes menulis untuk sistem warisan. Saya akan terus metrik tentang berapa banyak waktu yang dihabiskan dan berapa kali tes baru menangkap masalah (dan berapa kali mereka tidak). Saya akan kembali dan memperbarui ini sekitar satu tahun dari sekarang dengan hasil saya.

KESIMPULAN

Jadi ternyata pada dasarnya tidak mungkin untuk hanya menambahkan unit test ke kode yang ada dengan kemiripan ortodoksi. Setelah kode berfungsi, Anda jelas tidak dapat menerangi / menguji lampu hijau Anda, biasanya tidak jelas perilaku mana yang penting untuk diuji, tidak jelas harus mulai dari mana dan tentu saja tidak jelas kapan Anda selesai. Benar-benar bahkan mengajukan pertanyaan ini meleset dari titik utama dalam tes menulis. Dalam sebagian besar kasus, saya merasa lebih mudah menulis ulang kode menggunakan TDD daripada menguraikan fungsi yang dimaksud dan secara retroaktif menambahkan dalam unit test. Ketika memperbaiki masalah atau menambahkan fitur baru itu adalah cerita yang berbeda, dan saya percaya bahwa ini adalah waktu untuk menambahkan tes unit (seperti yang ditunjukkan beberapa di bawah). Akhirnya sebagian besar kode akan ditulis ulang, sering lebih cepat dari yang Anda harapkan - mengambil pendekatan ini I '

Paul
sumber
10
Menambahkan tes tidak akan merusak kode yang ada.
Dan Pichelman
30
@DanPichelman Anda belum pernah mengalami schroedinbug - "Sebuah bug desain atau implementasi dalam suatu program yang tidak terwujud sampai seseorang membaca sumber atau menggunakan program dengan cara yang tidak biasa memperhatikan bahwa itu tidak seharusnya bekerja, pada titik mana program dengan segera berhenti bekerja untuk semua orang sampai diperbaiki. "
8
@MichaelT Sekarang setelah Anda menyebutkannya, saya pikir saya telah melihat satu atau dua dari itu. Komentar saya seharusnya sudah membaca "Menambahkan tes biasanya tidak akan merusak kode yang ada". Terima kasih
Dan Pichelman
3
Hanya tulis tes di sekitar hal-hal yang ingin Anda refactor atau ubah.
Steven Evers
3
Periksa buku "Bekerja Efektif dengan Kode Warisan": amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/… , Michael Feathers menyarankan untuk menulis tes sebelum mengubah kode warisan apa pun.
Skarab

Jawaban:

68

Meskipun tes adalah ide yang bagus, niat pembuat kode asli untuk membuatnya adalah ketika ia sedang membangun aplikasi untuk menangkap pengetahuannya tentang bagaimana kode seharusnya bekerja dan apa yang mungkin rusak, yang kemudian akan ditransfer ke Anda.

Dalam mengambil pendekatan ini, ada kemungkinan besar bahwa Anda akan menulis tes yang paling tidak mungkin putus, dan kehilangan sebagian besar kasus tepi yang akan ditemukan saat membangun aplikasi.

Masalahnya adalah bahwa sebagian besar nilai akan datang dari 'gotcha' dan situasi yang kurang jelas. Tanpa tes tersebut, test suite kehilangan hampir semua efektivitasnya. Selain itu, perusahaan akan memiliki rasa aman palsu di sekitar aplikasi mereka, karena tidak akan menjadi bukti regresi yang lebih signifikan.

Biasanya cara untuk menangani jenis basis kode ini adalah dengan menulis tes untuk kode baru dan untuk refactoring kode lama sampai basis kode warisan sepenuhnya direaktor ulang.

Juga melihat .

FMJaguar
sumber
11
Tapi, bahkan tanpa TDD, unit test berguna saat refactoring.
pdr
1
Jika kode terus bekerja dengan baik, itu bukan masalah, tetapi hal terbaik untuk dilakukan adalah menguji antarmuka ke kode lama setiap kali Anda menulis sesuatu yang bergantung pada perilakunya.
deworde
1
Inilah sebabnya mengapa Anda mengukur cakupan tes . Jika tes untuk bagian tertentu tidak mencakup semua if dan elses dan semua kasus tepi maka Anda tidak dapat dengan aman memperbaiki bagian itu. Cakupan akan memberi tahu Anda jika semua saluran terkena sehingga tujuan Anda adalah meningkatkan cakupan sebanyak mungkin sebelum refactoring.
Rudolf Olah
3
Kelemahan utama TDD adalah bahwa sementara suite dapat berjalan, bagi pengembang yang tidak terbiasa dengan basis kode, ini adalah rasa aman yang salah. BDD jauh lebih baik dalam hal ini karena outputnya adalah maksud dari kode dalam bahasa Inggris.
Robbie Dee
3
Sama seperti untuk menyebutkan bahwa cakupan kode 100% tidak berarti kode Anda berfungsi dengan benar 100% dari waktu. Anda dapat meminta setiap baris kode untuk metode diuji tetapi hanya karena ia bekerja dengan value1 tidak berarti dijamin untuk bekerja dengan value2.
ryanzec
35

Ya, menambahkan tes jelas merupakan ide yang bagus.

Anda mengatakan bahwa itu didokumentasikan dengan baik, dan itu menempatkan Anda dalam posisi yang baik. Coba buat tes menggunakan dokumentasi itu sebagai panduan, dengan fokus pada bagian-bagian sistem yang kritis atau sering berubah.

Awalnya, ukuran tipis basis kode mungkin akan tampak luar biasa dibandingkan dengan sejumlah kecil tes, tetapi tidak ada pendekatan big-bang, dan membuat permulaan di suatu tempat lebih penting daripada mengeluh tentang apa yang akan menjadi pendekatan terbaik.

Saya akan merekomendasikan buku Michael Feathers, Bekerja Efektif dengan Legacy Code , untuk beberapa saran yang bagus dan terperinci.

Danny Woods
sumber
10
+1. Jika Anda menulis tes berdasarkan pada dokumentasi, Anda akan menemukan perbedaan antara kode kerja dan dokumentasi, dan itu sangat berharga.
Carl Manaster
1
Alasan lain untuk menambahkan tes: ketika bug ditemukan, Anda dapat dengan mudah menambahkan test case untuk pengujian regresi di masa depan.
Strategi ini pada dasarnya adalah yang diuraikan dalam kursus online (gratis) edX CS169.2x dalam bab tentang kode warisan. Seperti yang dikatakan oleh para guru: "Membangun Uji Kebenaran dengan Uji Karakterisasi" di Bab 9 dalam buku: beta.saasbook.info/table-of-contents
FGM
21

Tidak semua unit test memiliki manfaat yang sama. Manfaat dari tes unit datang ketika gagal. Semakin kecil kemungkinan gagal, semakin tidak menguntungkan itu. Kode baru atau yang baru berubah lebih cenderung mengandung bug daripada kode yang jarang berubah yang diuji dengan baik dalam produksi. Oleh karena itu, tes unit pada kode baru atau yang baru saja diubah lebih cenderung lebih menguntungkan.

Tidak semua unit test memiliki biaya yang sama. Jauh lebih mudah untuk menguji unit kode sepele yang Anda rancang sendiri hari ini daripada kode kompleks yang dirancang orang lain sejak dulu. Juga, pengujian selama pengembangan biasanya menghemat waktu pengembangan. Pada kode lama, penghematan biaya tidak lagi tersedia.

Di dunia yang ideal, Anda akan memiliki semua waktu yang Anda butuhkan untuk menguji kode warisan, tetapi di dunia nyata, pada titik tertentu masuk akal bahwa biaya menambahkan pengujian unit ke kode warisan akan lebih besar daripada manfaatnya. Kuncinya adalah mengidentifikasi titik itu. Kontrol versi Anda dapat membantu dengan menunjukkan kepada Anda kode yang paling baru diubah dan paling sering diubah, dan Anda bisa mulai dengan meletakkannya di bawah unit test. Juga, ketika Anda membuat perubahan ke depan, letakkan perubahan itu dan kode yang terkait erat di bawah unit test.

Dengan mengikuti metode itu, pada akhirnya Anda akan memiliki jangkauan yang cukup baik di bidang yang paling menguntungkan. Jika Anda menghabiskan waktu berbulan-bulan untuk mendapatkan unit test sebelum memulai kembali kegiatan yang menghasilkan pendapatan, itu mungkin merupakan keputusan pemeliharaan perangkat lunak yang diinginkan, tetapi itu adalah keputusan bisnis yang buruk.

Karl Bielefeldt
sumber
3
Jika kode jarang gagal, banyak waktu dan usaha dapat dikeluarkan untuk menemukan masalah misterius yang tidak pernah terjadi dalam kehidupan nyata. Di sisi lain, jika kodenya bermasalah dan rentan kesalahan, Anda mungkin dapat mulai menguji di mana saja dan menemukan masalah dengan segera.
Robbie Dee
8

Apakah menambahkan tes merupakan ide yang bagus?

Tentu saja, meskipun saya merasa sedikit sulit untuk percaya bahwa kode itu bersih dan berfungsi dengan baik dan menggunakan teknik modern dan tidak memiliki unit test. Apakah Anda yakin mereka tidak duduk dalam solusi terpisah?

Ngomong-ngomong, jika Anda akan memperpanjang / memelihara kode maka tes unit yang sebenarnya sangat berharga untuk proses itu.

Jika demikian, bagaimana seseorang memulai sesuatu seperti ini?

Satu langkah pada satu waktu. Jika Anda tidak terbiasa dengan pengujian unit, maka pelajari sedikit. Setelah Anda nyaman dengan konsepnya, pilih satu bagian kecil dari kode dan tulis tes untuknya. Lalu selanjutnya, dan selanjutnya. Cakupan kode dapat membantu Anda menemukan tempat yang Anda lewatkan.

Mungkin terbaik untuk memilih hal-hal berbahaya / berisiko / vital untuk diuji terlebih dahulu, tetapi Anda mungkin lebih efektif menguji sesuatu langsung untuk masuk ke alur terlebih dahulu - terutama jika Anda / tim tidak terbiasa dengan basis kode dan / atau unit pengujian.

Telastyn
sumber
7
"Apakah kamu yakin mereka tidak duduk dalam solusi terpisah?" adalah pertanyaan yang bagus. Saya harap OP tidak mengabaikannya.
Dan Pichelman
Sayangnya, tidak ada kesempatan, aplikasi itu dimulai bertahun-tahun yang lalu ketika TDD mendapatkan daya tarik, jadi tujuannya adalah untuk melakukan tes di beberapa titik, tetapi untuk beberapa alasan begitu proyek dimulai mereka tidak pernah mendapatkannya.
Paul
2
Mereka mungkin tidak pernah melakukannya karena itu akan memakan waktu ekstra bagi mereka yang tidak sepadan. Pengembang yang baik yang bekerja sendiri pasti dapat membuat aplikasi yang bersih, jelas, terorganisir dengan baik, dan bekerja dengan ukuran yang relatif besar tanpa tes otomatis, dan umumnya mereka dapat melakukannya dengan lebih cepat dan tanpa masalah seperti halnya dengan pengujian. Karena semuanya ada di kepala mereka sendiri, ada kemungkinan bug atau masalah organisasi yang jauh lebih rendah dibandingkan dengan beberapa pengembang yang membuatnya.
Ben Lee
3

Ya, melakukan tes adalah ide yang bagus. Mereka akan membantu mendokumentasikan karya basis kode yang ada sebagaimana dimaksud dan menangkap perilaku tak terduga. Bahkan jika tes pada awalnya gagal, biarkan, dan kemudian refactor kode nanti sehingga mereka lulus dan berperilaku sebagaimana dimaksud.

Mulailah menulis tes untuk kelas yang lebih kecil (yang tidak memiliki dependensi dan relatif sederhana) dan beralih ke kelas yang lebih besar (yang memiliki dependensi dan lebih kompleks). Ini akan memakan waktu lama, tetapi bersabarlah dan gigih sehingga Anda akhirnya dapat menutupi basis kode sebanyak mungkin.

Bernard
sumber
Apakah Anda benar-benar menambahkan tes gagal ke program yang berfungsi dengan baik (kata OP)?
MarkJ
Ya, karena itu menunjukkan bahwa ada sesuatu yang tidak berfungsi sebagaimana dimaksud dan memerlukan peninjauan lebih lanjut. Ini akan memicu diskusi dan semoga memperbaiki kesalahpahaman atau cacat yang sebelumnya tidak diketahui.
Bernard
@Bernard - atau, tes dapat mengekspos kesalahpahaman Anda tentang apa yang seharusnya dilakukan oleh kode. Tes yang ditulis setelah fakta menjalankan risiko tidak benar merangkum niat asli.
Dan Pichelman
@DanPichelman: Setuju, tetapi ini seharusnya tidak membuat orang tidak mau menulis tes apa pun.
Bernard
Jika tidak ada yang lain, itu akan menunjukkan bahwa kode belum ditulis secara defensif.
Robbie Dee
3

OK, saya akan memberikan pendapat sebaliknya ....

Menambahkan tes ke sistem yang ada dan berfungsi akan mengubah sistem itu, kecuali jika itu, semua sistem ditulis dengan ejekan sejak awal. Saya ragu, meskipun sangat mungkin memiliki pemisahan yang baik dari semua komponen dengan batas-batas yang mudah didefinisikan sehingga Anda dapat memasukkan antarmuka tiruan Anda. Tetapi jika tidak, maka Anda harus membuat perubahan yang cukup signifikan (secara relatif) yang dapat merusak banyak hal. Dalam kasus terbaik, Anda akan menghabiskan banyak waktu untuk menulis tes ini, waktu yang bisa lebih baik dihabiskan untuk menulis dokumen desain rinci, dokumen analisis dampak atau dokumen konfigurasi solusi sebagai gantinya. Lagi pula, itulah pekerjaan yang diinginkan bos Anda lebih dari sekadar tes unit. Bukan?

Lagi pula, saya tidak akan menambahkan tes unit apa pun.

Saya akan berkonsentrasi pada eksternal, alat pengujian otomatis yang akan memberi Anda cakupan yang wajar tanpa mengubah apa pun. Kemudian, ketika Anda datang untuk membuat modifikasi ... saat itulah Anda dapat mulai menambahkan unit test di dalam basis kode.

gbjbaanb
sumber
2

Saya sering mengalami situasi ini, mewarisi basis kode besar tanpa cakupan tes yang memadai atau tidak ada, dan sekarang saya bertanggung jawab untuk menambahkan fungsionalitas, memperbaiki bug, dll.

Saran saya adalah memastikan dan menguji apa yang Anda tambahkan, dan jika Anda memperbaiki bug atau mengubah kasus penggunaan dalam kode saat ini, penulis mengujinya. Jika Anda harus menyentuh sesuatu, tulis tes pada titik itu.

Ketika ini rusak adalah ketika kode yang ada tidak terstruktur dengan baik untuk pengujian unit, jadi Anda menghabiskan banyak waktu untuk refactoring sehingga Anda dapat menambahkan tes untuk perubahan kecil.

Casey
sumber