Bagaimana meningkatkan cakupan kode secara drastis?

21

Saya ditugaskan untuk mendapatkan aplikasi warisan di bawah unit test. Pertama beberapa latar belakang tentang aplikasi: Ini adalah 600k LOC Java RCP kode dasar dengan masalah-masalah utama

  • duplikasi kode besar-besaran
  • tanpa enkapsulasi, sebagian besar data pribadi dapat diakses dari luar, beberapa data bisnis juga dibuat lajang sehingga tidak hanya dapat diubah dari luar tetapi juga dari mana-mana.
  • tidak ada abstraksi (mis. tidak ada model bisnis, data bisnis disimpan di Object [] dan double [] []), jadi tidak ada OO.

Ada rangkaian uji regresi yang baik dan tim QA yang efisien menguji dan menemukan bug. Saya tahu teknik bagaimana mendapatkannya dari buku-buku klasik, misalnya Michael Feathers, tetapi itu terlalu lambat. Karena ada sistem uji regresi yang berfungsi, saya tidak takut untuk secara agresif memperbaiki sistem untuk memungkinkan tes unit ditulis.

Bagaimana saya harus mulai menyerang masalah untuk mendapatkan liputan dengan cepat , jadi saya dapat menunjukkan kemajuan ke manajemen (dan sebenarnya mulai mendapatkan penghasilan dari jaring pengaman tes JUnit)? Saya tidak ingin menggunakan alat untuk menghasilkan suite tes regresi, misalnya AgitarOne, karena tes ini tidak menguji apakah ada sesuatu yang benar.

Peter Kofler
sumber
Mengapa tidak membuat tes regresi secara otomatis, dan verifikasi masing-masing secara individual? Harus lebih cepat daripada menulis semuanya dengan tangan.
Robert Harvey
Kedengarannya agak lucu menyebut sesuatu yang ditulis dalam Java Legacy, tetapi setuju, itu tentu saja adalah legacy. Anda menyebutkan bahwa Anda tidak takut untuk melakukan refactor pada sistem agar tes unit dapat ditulis, tetapi bukankah seharusnya Anda menulis tes unit pada sistem seperti apa adanya, sebelum ada upaya refactoring yang dilakukan? Kemudian refactoring Anda dapat dijalankan melalui tes unit yang sama untuk memastikan tidak ada yang rusak?
dodgy_coder
1
@dodgy_coder Biasanya setuju, tapi saya berharap QA tradisional yang bekerja secara efisien akan menyelamatkan saya beberapa saat.
Peter Kofler
1
@dodgy_coder Michael C. Feathers, penulis Bekerja Efektif dengan kode Legacy mendefinisikan kode Legacy sebagai "kode tanpa tes." Ini berfungsi sebagai definisi yang bermanfaat.
StuperUser

Jawaban:

10

Saya percaya ada dua sumbu utama di mana kode dapat ditempatkan ketika datang untuk memperkenalkan unit test: A) seberapa teruji kodenya? dan B) seberapa stabil itu (yaitu seberapa mendesak itu perlu tes)? Hanya dengan melihat ekstrem, ini menghasilkan 4 kategori:

  1. Kode itu mudah diuji dan rapuh
  2. Kode yang mudah diuji dan stabil
  3. Kode yang sulit untuk diuji dan rapuh
  4. Kode yang sulit untuk diuji dan stabil

Kategori 1 adalah tempat yang jelas untuk memulai, di mana Anda bisa mendapatkan banyak manfaat dengan kerja yang relatif sedikit. Kategori 2 memungkinkan Anda untuk dengan cepat meningkatkan statistik cakupan Anda (baik untuk moral) dan mendapatkan lebih banyak pengalaman dengan basis kode, sementara kategori 3 lebih (sering membuat frustrasi) bekerja tetapi juga menghasilkan lebih banyak manfaat. Yang harus Anda lakukan pertama tergantung pada seberapa penting semangat dan cakupan statistik bagi Anda. Kategori 4 mungkin tidak layak untuk diganggu.

Michael Borgwardt
sumber
Luar biasa. Saya punya ide bagaimana menentukan apakah mudah untuk memeriksa dengan analisis statis, misalnya jumlah dependensi / Testability Explorer. Tapi bagaimana saya bisa menentukan apakah kode rapuh? Saya tidak dapat mencocokkan cacat ke unit tertentu (misalnya kelas), dan jika itu nomor 3 tentu saja (kelas dewa / lajang). Jadi mungkin jumlah checkin (hotspot)?
Peter Kofler
1
@ Peter Kofler: melakukan hotspot adalah ide yang bagus, tetapi sumber paling berharga dari pengetahuan semacam ini adalah pengembang yang telah bekerja dengan kode tersebut.
Michael Borgwardt
1
@ Peter - seperti Michael berkata, para pengembang yang telah bekerja dengan kode. Siapa pun yang telah bekerja dengan basis kode besar untuk waktu yang cukup lama akan tahu bagian mana dari baunya. Atau, jika semuanya bau, bagian mana dari itu benar-benar bau busuk .
Carson63000
15

Saya punya banyak pengalaman bekerja pada sistem legacy (bukan Java sekalipun), jauh lebih besar dari ini. Saya benci menjadi pembawa berita buruk, masalah Anda adalah ukuran masalah Anda. Saya menduga Anda telah meremehkannya.

Menambahkan tes regresi ke kode lawas adalah proses yang lambat dan mahal. Banyak persyaratan tidak terdokumentasi dengan baik - perbaikan bug di sini, patch di sana, dan sebelum Anda menyadarinya, perangkat lunak menentukan perilaku itu sendiri. Tidak memiliki tes berarti bahwa implementasi adalah semua yang harus dilalui, tidak ada tes untuk "menantang" persyaratan implisit yang diterapkan dalam kode.

Jika Anda mencoba untuk mendapatkan perlindungan dengan cepat, kemungkinan Anda akan terburu-buru dalam pekerjaan, setengah memanggangnya, dan gagal. Tes akan memberikan cakupan sebagian dari hal-hal yang jelas, dan miskin untuk tidak ada liputan dari masalah nyata. Anda akan meyakinkan para manajer yang ingin Anda jual ke Unit Testing tidak layak, bahwa itu hanyalah peluru perak yang tidak berfungsi.

IMHO, Pendekatan terbaik adalah menargetkan Anda menguji. Gunakan metrik, firasat dan cacat laporan log untuk mengidentifikasi 1% atau 10% kode yang menghasilkan masalah terbanyak. Pukul modul-modul ini dengan keras, dan abaikan sisanya. Jangan mencoba melakukan terlalu banyak, lebih sedikit lebih banyak.

Tujuan yang realistis adalah "Sejak kami menerapkan UT, penyisipan cacat dalam modul yang diuji telah turun menjadi x% dari yang tidak di bawah UT" (idealnya x adalah angka <100).

mattnz
sumber
+1, Anda tidak dapat menguji unit secara efektif tanpa standar yang lebih kuat daripada kode.
dan_waterworth
Saya tahu dan saya setuju. Perbedaannya adalah kita memiliki tes, pengujian regresi tradisional dengan bekerja QA di tempat, sehingga ada beberapa jenis jaring pengaman. Kedua saya sangat mendukung tes unit, jadi pasti tidak akan menjadi hal lain yang tidak berhasil. Poin bagus tentang apa yang harus ditargetkan terlebih dahulu. Terima kasih.
Peter Kofler
1
dan jangan lupa bahwa hanya bertujuan untuk "cakupan" tidak akan meningkatkan kualitas karena Anda akan terjebak dalam tumpukan tes yang cacat dan sepele (dan tes untuk kode sepele yang tidak memerlukan pengujian eksplisit, tetapi ditambahkan hanya untuk meningkatkan cakupan). Anda akan akhirnya membuat tes untuk menyenangkan alat peliputan, bukan karena mereka berguna, dan mungkin akan mengubah kode itu sendiri untuk meningkatkan cakupan tes tanpa menulis tes (seperti memotong komentar dan definisi variabel, yang beberapa alat peliputan akan memanggil kode yang tidak terbuka).
jwenting
2

Saya teringat pepatah itu tentang tidak khawatir tentang pintu gudang ketika kuda sudah melesat.

Kenyataannya adalah bahwa sebenarnya tidak ada cara yang hemat biaya untuk mendapatkan cakupan pengujian yang baik untuk sistem warisan, tentu saja tidak dalam jangka waktu singkat. Seperti yang disebutkan MattNz, ini akan menjadi proses yang sangat memakan waktu, dan pada akhirnya sangat mahal. Naluri saya memberi tahu saya bahwa jika Anda mencoba melakukan sesuatu untuk mengesankan manajemen, Anda mungkin akan membuat mimpi buruk pemeliharaan baru karena mencoba menunjukkan terlalu banyak terlalu cepat, tanpa sepenuhnya memahami persyaratan yang Anda coba uji.

Secara realistis, begitu Anda telah menulis kode, hampir terlambat untuk menulis tes secara efektif tanpa risiko kehilangan sesuatu yang vital. Di sisi lain, Anda bisa mengatakan bahwa beberapa tes lebih baik daripada tidak ada tes, tetapi jika itu masalahnya, tes itu sendiri perlu menunjukkan bahwa mereka menambah nilai pada sistem secara keseluruhan.

Saran saya adalah untuk melihat bidang-bidang utama di mana Anda merasa ada sesuatu yang "rusak". Maksud saya itu bisa menjadi kode yang sangat tidak efisien, atau yang dapat Anda tunjukkan sebelumnya mahal untuk dipelihara. Dokumentasikan masalah, kemudian gunakan ini sebagai titik awal untuk memperkenalkan tingkat pengujian yang membantu Anda meningkatkan sistem, tanpa memulai upaya rekayasa ulang besar-besaran. Idenya di sini adalah untuk menghindari bermain dengan tes, dan bukannya memperkenalkan tes untuk membantu Anda memecahkan masalah tertentu. Setelah jangka waktu tertentu, lihat apakah Anda dapat mengukur dan membedakan antara biaya sebelumnya untuk mempertahankan bagian kode itu, dan upaya saat ini dengan perbaikan yang telah Anda terapkan dengan tes pendukungnya.

Yang perlu diingat adalah bahwa manajemen lebih tertarik pada biaya / manfaat dan bagaimana hal itu secara langsung mempengaruhi pelanggan mereka, dan akhirnya anggaran mereka. Mereka tidak pernah tertarik untuk melakukan sesuatu hanya karena itu adalah hal terbaik untuk dilakukan kecuali Anda dapat membuktikan bahwa itu akan memberi mereka manfaat yang menarik bagi mereka. Jika Anda dapat menunjukkan bahwa Anda meningkatkan sistem dan mendapatkan cakupan tes yang baik untuk pekerjaan yang sedang Anda lakukan saat ini, manajemen cenderung melihat ini sebagai aplikasi yang efisien dari upaya Anda. Ini bisa memungkinkan Anda untuk memperdebatkan kasus untuk memperluas upaya Anda ke bidang-bidang utama lainnya, tanpa menuntut pembekuan lengkap pengembangan produk, atau lebih buruk lagi, hampir mustahil untuk berdebat untuk ditulis ulang!

S.Robins
sumber
1

Salah satu cara untuk meningkatkan cakupan adalah dengan menulis lebih banyak tes.

Cara lain adalah dengan mengurangi redundansi dalam kode Anda, sedemikian rupa sehingga tes yang ada berlaku mencakup kode redundan yang saat ini tidak tercakup.

Bayangkan Anda memiliki 3 blok kode, a, b, dan b ', di mana b' adalah duplikat (salinan persis atau hampir ketinggalan) dari B, dan bahwa Anda memiliki cakupan pada a dan b tetapi tidak b 'dengan tes T.

Jika refactor basis kode untuk menghilangkan b 'dengan mengekstraksi kesamaan dari b dan b' sebagai B, basis kode sekarang terlihat seperti a, b0, B, b'0, dengan b0 yang berisi kode nonshared dengan b'0 dan sebaliknya sebaliknya, dan kedua b0 dan b'0 menjadi jauh lebih kecil dari B, dan memanggil / menggunakan B.

Sekarang fungsi program tidak berubah, dan tidak ada yang menguji T, jadi kita bisa menjalankan T lagi dan berharap lulus. Kode yang sekarang dibahas adalah a, b0, dan B, tetapi tidak b'0. Basis kode menjadi lebih kecil (b'0 lebih kecil dari b '!) Dan kami masih membahas apa yang awalnya kami bahas. Rasio cakupan kami telah naik.

Untuk melakukan ini, Anda perlu menemukan b, b 'dan idealnya B untuk mengaktifkan refactoring Anda. Alat CloneDR kami dapat melakukan ini untuk banyak bahasa, terutama termasuk Java. Anda mengatakan basis kode Anda memiliki banyak kode duplikat; ini mungkin cara yang baik untuk mengatasinya demi keuntungan Anda.

Anehnya, tindakan menemukan b dan b 'sering meningkatkan kosakata Anda tentang ide-ide abstrak yang diterapkan oleh program. Sementara alat tidak tahu apa b dan b 'lakukan, tindakan mengisolasi mereka dari kode, memungkinkan fokus sederhana pada konten b dan b', sering memberi programmer ide yang sangat baik tentang apa abstraksi B_abstrak penerapan kode kloning . Jadi ini meningkatkan pemahaman Anda tentang kode juga. Pastikan Anda memberi B nama yang baik ketika Anda membatalkannya, dan Anda akan mendapatkan cakupan tes yang lebih baik, dan program yang lebih dapat dipertahankan.

Ira Baxter
sumber