Cara menghapus fungsi atau fitur saat menggunakan TDD

20

Dalam teks tentang TDD saya sering membaca tentang "menghapus duplikasi" atau "meningkatkan keterbacaan" selama langkah refactoring. Tapi apa yang membuat saya menghapus fungsi yang tidak digunakan?

Sebagai contoh katakanlah ada kelas Cdengan metode a()dan b(). Sekarang saya pikir akan menyenangkan untuk memiliki metode f()yang didorong ke dalamnya C. Bahkan f()menggantikan semua panggilan b()dengan pengecualian unit test yang didefinisikan / dijelaskan b(). Tidak diperlukan lagi - kecuali untuk tes.

Apakah hanya menyimpan untuk menghapus b()dan semua tes yang menggunakannya? Apakah itu bagian dari "meningkatkan keterbacaan"?

TobiMcNamobi
sumber
3
Cukup tambahkan tes lain untuk memeriksa bahwa fungsi tersebut tidak ada, dan kemudian perbaiki gagal testcases;)
Filip Haglund
@FilipHaglund Tergantung pada bahasanya ini mungkin. Tetapi sebagai dokumentasi kode ini akan terlihat aneh.
TobiMcNamobi
1
Hanya untuk siapa saja yang mungkin tidak tahu yang lebih baik: Komentar @ FilipHaglund jelas merupakan lelucon. Jangan lakukan itu.
jhyot

Jawaban:

16

Ya tentu saja. Kode yang paling mudah dibaca adalah yang tidak ada.

Yang mengatakan, refactoring umumnya berarti memperbaiki kode tanpa mengubah perilakunya. Jika Anda memikirkan sesuatu yang meningkatkan kode, lakukan saja. Anda tidak perlu memasukkannya ke dalam lubang merpati sebelum Anda diizinkan melakukannya.

Sebastian Redl
sumber
@ jpmc26 Agile, man, agile! :-)
TobiMcNamobi
1
"Kode yang paling mudah dibaca adalah yang tidak ada di sana." - Saya menggantung kutipan itu di dinding: D
winkbrace
27

Menghapus metode publik bukan "refactoring" - refactoring mengubah implementasi sambil terus melewati tes yang ada.

Namun, menghapus metode yang tidak dibutuhkan adalah perubahan desain yang masuk akal.

TDD menarik ini sampai batas tertentu, karena dalam meninjau tes, Anda dapat mengamati bahwa itu menguji metode yang tidak dibutuhkan. Tes mendorong desain Anda, karena Anda dapat pergi "Lihat, tes ini tidak ada hubungannya dengan tujuan saya".

Ini mungkin mengungkapkan dirinya lebih pada tingkat pengujian yang lebih tinggi, dalam hubungannya dengan alat cakupan kode. Jika Anda menjalankan tes integrasi dengan cakupan kode, dan melihat bahwa metode tidak dipanggil, itu adalah petunjuk bahwa metode tidak digunakan. Analisis kode statis juga dapat menunjukkan bahwa metode tidak digunakan.

Ada dua pendekatan untuk menghapus suatu metode; keduanya bekerja dalam keadaan yang berbeda:

  1. Hapus metode. Ikuti kesalahan kompilasi, untuk menghapus kode dan tes yang bergantung. Jika Anda puas bahwa tes yang terpengaruh dapat dibuang, lakukan perubahan Anda. Jika tidak, putar kembali.

  2. Hapus tes yang Anda rasa sudah usang. Jalankan seluruh rangkaian uji Anda dengan cakupan kode. Hapus metode yang belum dilakukan oleh test suite.

(Ini mengandaikan bahwa ruang tes Anda memiliki cakupan yang baik untuk memulai).

ramping
sumber
10

Faktanya f () menggantikan semua panggilan ke b () dengan pengecualian unit test yang didefinisikan / dijelaskan b ()

IMHO, siklus TDD khas akan terlihat seperti ini:

  • tulis gagal tes untuk f () (mungkin berdasarkan tes untuk b ()): tes menjadi merah

  • implementasikan uji f () -> menjadi hijau

  • refactor : -> hapus b () dan semua tes untuk b ()

Untuk langkah terakhir, Anda dapat mempertimbangkan untuk menghapus b () terlebih dahulu dan melihat apa yang terjadi (ketika menggunakan bahasa yang dikompilasi, kompiler hanya akan mengeluh tentang tes yang ada, ketika tidak, tes unit lama untuk b akan gagal, jadi itu adalah jelas Anda harus menghapusnya juga).

Doc Brown
sumber
4

Ya itu.

Kode terbaik, paling bebas bug, dan paling mudah dibaca adalah kode yang tidak ada. Berusaha keras untuk menulis sebanyak mungkin non-kode sambil memenuhi persyaratan Anda.

Kilian Foth
sumber
9
Anda melewatkan bagian "bagaimana".
JeffO
2

Ini diinginkan untuk dihapus b()setelah tidak lagi digunakan, karena alasan yang sama diinginkan untuk tidak menambahkan fungsi yang tidak digunakan di tempat pertama. Apakah Anda menyebutnya "keterbacaan" atau sesuatu yang lain, semua yang dianggap sama itu merupakan peningkatan pada kode yang tidak mengandung apa pun yang tidak ada gunanya. Demi memiliki setidaknya satu ukuran spesifik yang lebih baik tidak memilikinya, menghapusnya menjamin bahwa biaya pemeliharaan di masa depan setelah perubahan itu nol!

Saya belum menemukan teknik khusus yang diperlukan untuk benar-benar menghapusnya dengan tes, karena ada pemikiran untuk mengganti b() dengan sesuatu yang baru tentu saja harus disertai dengan pertimbangan semua kode yang sedang dipanggil b(), dan tes adalah bagian dari "semua kode ".

Garis penalaran yang umumnya bekerja untuk saya adalah bahwa pada titik di mana saya perhatikan yang f()telah menjadi b()usang, karena itu b()harus paling tidak usang, dan saya mencari untuk menemukan semua panggilan b()dengan maksud untuk menggantikannya dengan panggilan ke f(), saya pertimbangkan juga kode tes . Secara khusus, jikab() tidak lagi diperlukan maka saya bisa dan harus menghapus tes unitnya.

Anda benar bahwa tidak ada yang memaksa saya untuk memperhatikan bahwa b()tidak lagi diperlukan. Itu masalah keterampilan (dan, seperti kata ramping, laporan cakupan kode pada tes tingkat yang lebih tinggi). Jika hanya tes unit, dan tidak ada tes fungsional, lihat b(), maka saya bisa optimis dengan hati-hati bahwa itu bukan bagian dari antarmuka yang diterbitkan dan oleh karena itu menghapus itu bukan perubahan yang melanggar untuk kode apa pun yang tidak di bawah kendali langsung saya.

Siklus merah / hijau / refactor tidak secara eksplisit menyebutkan tes pelepasan. Selanjutnya, menghapus b()melanggar terbuka / tertutup prinsip karena jelas komponen Anda adalah terbuka untuk modifikasi. Jadi jika Anda ingin menganggap langkah ini sebagai sesuatu di luar TDD sederhana, silakan. Misalnya, Anda mungkin memiliki beberapa proses untuk mendeklarasikan tes "buruk", yang dapat diterapkan dalam kasus ini untuk menghapus tes dengan alasan tes untuk sesuatu yang seharusnya tidak ada di sana (fungsi yang tidak perlu)b() ).

Saya pikir dalam praktiknya kebanyakan orang mungkin mengizinkan sejumlah desain ulang untuk dilakukan bersamaan dengan siklus merah / hijau / refactor, atau mereka menganggap menghapus tes unit redundan sebagai bagian valid dari "refactor" meskipun secara tegas berbicara itu bukan refactoring. Tim Anda dapat memutuskan berapa banyak drama dan dokumen yang harus dilibatkan dalam membenarkan keputusan ini.

Lagi pula, jika b()itu penting maka akan ada tes fungsional untuk itu, dan itu tidak akan dihapus ringan, tetapi Anda sudah mengatakan bahwa hanya ada tes unit. Jika Anda tidak benar membedakan antara tes unit (ditulis dengan desain internal kode saat ini, yang telah Anda ubah) dan tes fungsional (ditulis ke antarmuka yang diterbitkan, yang mungkin Anda tidak ingin ubah) maka Anda harus lebih berhati-hati tentang menghapus unit test.

Steve Jessop
sumber
2

Satu hal yang harus selalu diingat adalah bahwa kita sekarang menggunakan REPOSITORI KODE dengan VERSION CONTROL. Kode yang dihapus itu tidak benar-benar hilang ... masih ada di suatu tempat di iterasi sebelumnya. Jadi hancurkan! Jadilah liberal dengan kunci hapus, karena Anda selalu dapat kembali dan mengambil metode elegan yang berharga yang Anda pikir mungkin berguna suatu hari nanti ... jika suatu hari nanti pernah datang. Itu disana.

Tentu saja, itu sejalan dengan peringatan dari penyakit dan bahaya rilis yang tidak kompatibel dengan belakang ... aplikasi eksternal yang bergantung pada implementasi antarmuka Anda, yang sekarang menjadi yatim piatu dengan kode Anda yang tiba-tiba (usang).

dwoz
sumber
Menghapus kode tidak menjadi masalah secara umum. Saya suka menghapus untuk menghapus baris, fungsi, atau ... well, seluruh modul! Jika memungkinkan. Dan saya melakukan ini semua dengan atau tanpa TDD. Tetapi: Apakah ada titik dalam alur kerja TDD di mana kode (non-dupli) dihapus? Jika tidak, itu akan tetap dihapus. Tetapi apakah ada? Itu pertanyaan saya.
TobiMcNamobi