Pengujian hanya melalui metode publik itu buruk

Dalam pemrograman dan TDD, khususnya, ada prinsip-prinsip baik yang berguna untuk mematuhi: KERING dan pengujian melalui metode publik. Mereka berulang kali membuktikan diri dalam praktik, tetapi dalam proyek dengan kode warisan besar mereka dapat memiliki "sisi gelap". Misalnya, Anda dapat menulis kode yang dipandu oleh prinsip-prinsip ini, dan kemudian menemukan diri Anda membongkar tes yang mencakup sekelompok 20+ abstraksi dengan konfigurasi yang jauh lebih besar daripada logika yang diuji. "Sisi gelap" ini membuat orang takut dan menghambat penggunaan TDD dalam proyek. Di bawah potongan, saya membahas mengapa pengujian melalui metode publik itu buruk dan bagaimana mengurangi masalah yang timbul karena prinsip ini.

Penafian
Segera saya ingin menghilangkan kesan yang mungkin. Beberapa bahkan mungkin tidak merasakan kekurangan yang akan dibahas, karena, misalnya, ukuran proyek mereka. Juga, kekurangan-kekurangan ini, menurut saya, adalah bagian dari hutang teknis dan memiliki karakteristik yang sama: masalahnya akan bertambah jika tidak diperhatikan. Oleh karena itu, perlu untuk memutuskan sesuai situasi.

Gagasan yang mendasari prinsip itu terdengar bagus: Anda perlu menguji perilaku, bukan implementasi. Ini berarti Anda hanya perlu menguji antarmuka kelas. Dalam praktiknya, ini tidak selalu terjadi. Untuk menyajikan inti masalah, bayangkan Anda memiliki metode yang menghitung biaya pekerja yang terlibat dalam pekerjaan shift. Ini adalah tugas non-sepele ketika datang untuk shift kerja, sebagai mereka memiliki kiat, bonus, akhir pekan, liburan, peraturan perusahaan, dll., dll. Metode ini melakukan banyak operasi secara internal dan menggunakan layanan lain yang memberikan informasi tentang liburan, kiat, dll. Dengan demikian saat menulis tes unit untuk itu, perlu untuk membuat konfigurasi untuk semua layanan yang digunakan, jika kode yang diuji di suatu tempat di akhir metode. Pada saat yang sama, kode yang diuji sendiri hanya dapat menggunakan sebagian, atau tidak menggunakan layanan yang dapat dikonfigurasi sama sekali. Dan sudah ada beberapa unit test yang ditulis dengan cara ini.

Minus 1: Konfigurasi Uji Unit Lebih Tinggi


Sekarang Anda ingin menambahkan reaksi ke fitur baru yang memiliki logika non-sepele dan juga digunakan di suatu tempat di akhir metode. Sifat bendera adalah sedemikian rupa sehingga merupakan bagian dari logika layanan dan, pada saat yang sama, bukan bagian dari antarmuka layanan. Dalam kasus di atas, kode ini hanya relevan untuk metode publik ini, dan umumnya dapat ditulisi di dalam metode lama.

Jika proyek mengadopsi aturan untuk menguji semuanya hanya melalui metode publik, maka pengembang kemungkinan besar hanya dapat menyalin beberapa unit test yang ada dan sedikit mengubahnya. Dalam pengujian baru, masih akan ada konfigurasi semua layanan untuk menjalankan metode. Di satu sisi, kami mematuhi prinsip, tetapi, di sisi lain, kami mendapat tes unit dengan konfigurasi yang berlebihan. Di masa depan, jika ada yang rusak, atau membutuhkan perubahan konfigurasi, Anda harus melakukan pekerjaan monyet untuk menyesuaikan tes. Itu membosankan, panjang dan tidak membawa kebahagiaan atau manfaat nyata bagi klien. Tampaknya kita mengikuti prinsip yang benar, tetapi kita berada dalam situasi yang sama dengan yang ingin kita hindari, menolak untuk menguji metode pribadi.

Minus 2: Cakupan tidak lengkap


Selanjutnya faktor manusia seperti kemalasan dapat mengintervensi. Misalnya, metode pribadi dengan logika bendera non-sepele mungkin terlihat seperti dalam contoh ini.

private bool HasShifts(DateTime date, int tolerance, bool clockIn, Shift[] shifts, int[] locationIds) { bool isInLimit(DateTime date1, DateTime date2, int limit) => Math.Abs(date2.Subtract(date1).TotalMinutes) <= limit; var shiftsOfLocations = shifts.Where(x => locationIds.Contains(x.LocationId)); return clockIn ? shiftsOfLocations.Any(x => isInLimit(date, x.StartDate, tolerance)) : shiftsOfLocations.Any(x => isInLimit(date, x.EndDate, tolerance)); } 

Metode ini membutuhkan 10 cek untuk mencakup semua kasus, 8 di antaranya signifikan.

Decoding 8 kasus penting
  • shiftsOfLocations - 2 nilai - apakah atau tidak
  • clockIn - 2 nilai - benar atau salah
  • toleransi - 2 arti berbeda

Total: 2 x 2 x 2 = 8

Saat menulis tes unit untuk menguji logika ini, pengembang harus menulis setidaknya 8 tes unit besar. Saya menemukan kasus ketika konfigurasi unit test mengambil lebih dari 50 baris kode, dengan 4 baris panggilan langsung. Yaitu hanya sekitar 10% dari kode membawa muatan. Dalam hal ini, godaan sangat bagus untuk mengurangi jumlah pekerjaan dengan menulis lebih sedikit unit test. Akibatnya, dari 8, misalnya, hanya dua tes unit yang tersisa, untuk setiap nilai clockIn. Situasi ini mengarah pada fakta bahwa, sekali lagi, membosankan dan lama untuk menulis semua tes yang diperlukan, membuat konfigurasi (Ctrl + C, V berfungsi, di mana tanpanya), atau metode ini hanya sebagian yang tertutup. Setiap opsi memiliki konsekuensi yang tidak menyenangkan.

Kemungkinan solusi


Selain prinsip "perilaku uji", masih ada OCP (prinsip Terbuka / tertutup). Menerapkannya dengan benar, Anda dapat melupakan apa itu "tes rapuh", menguji perilaku internal modul. Jika Anda memerlukan perilaku modul baru, Anda akan menulis tes unit baru untuk kelas penerus baru di mana perilaku yang Anda butuhkan akan diubah. Maka Anda tidak perlu menghabiskan waktu untuk memeriksa ulang dan memperbarui tes yang ada. Dalam hal ini, metode ini dapat dinyatakan sebagai internal, atau internal yang dilindungi, dan diuji dengan menambahkan InternalsVisibleTo ke majelis. Dalam hal ini, antarmuka IClass Anda tidak akan menderita, dan tes akan menjadi yang paling singkat, tidak mengalami perubahan yang sering.

Alternatif lain adalah dengan mendeklarasikan kelas pembantu tambahan di mana metode kami dapat ditarik dengan menyatakannya sebagai publik. Kemudian prinsip akan diamati, dan tes akan singkat. Menurut pendapat saya, pendekatan ini tidak selalu membuahkan hasil. Sebagai contoh, beberapa mungkin memutuskan untuk menarik bahkan satu metode ke dalam satu kelas, yang mengarah pada penciptaan sekelompok kelas dengan satu metode. Ekstrem lainnya adalah membuang metode seperti itu ke dalam satu kelas penolong, yang berubah menjadi kelas penolong Tuhan. Tetapi opsi dengan helper ini mungkin satu-satunya jika rakitan kerja ditandatangani dengan nama yang kuat, dan Anda tidak dapat menandatangani rakitan uji, karena alasan tertentu. InternalsVisibleTo akan berfungsi ketika kedua majelis ditandatangani atau tidak sekaligus.

Ringkasan


Dan pada akhirnya, karena kombinasi masalah seperti itu, gagasan TDD dan tes unit menderita, karena tidak ada yang memiliki keinginan untuk menulis tes volumetrik dan mendukungnya. Saya akan senang melihat contoh bagaimana kepatuhan ketat terhadap prinsip ini menyebabkan masalah dan berkurangnya motivasi untuk menulis tes dari tim pengembangan.

Source: https://habr.com/ru/post/id419873/


All Articles