Saya sedang berupaya membuat kelas saya dapat diuji unit, menggunakan injeksi ketergantungan. Tetapi beberapa dari kelas-kelas ini memiliki banyak klien, dan saya belum siap untuk refactor mereka semua untuk mulai melewati dependensi. Jadi saya mencoba melakukannya secara bertahap; menjaga dependensi default untuk saat ini, tetapi membiarkannya ditimpa untuk pengujian.
Satu pendekatan saya conisdering hanya memindahkan semua panggilan "baru" ke metode mereka sendiri, misalnya:
public MyObject createMyObject(args) {
return new MyObject(args);
}
Kemudian dalam unit test saya, saya bisa mensubkelaskan kelas ini, dan mengesampingkan fungsi create, jadi mereka malah membuat objek palsu.
Apakah ini pendekatan yang baik? Apakah ada kerugiannya?
Lebih umum, apakah boleh memiliki dependensi hard-coded, selama Anda dapat menggantinya untuk pengujian? Saya tahu pendekatan yang disukai adalah secara eksplisit mengharuskan mereka dalam konstruktor, dan saya ingin sampai di sana pada akhirnya. Tapi saya bertanya-tanya apakah ini langkah pertama yang baik.
Satu kekurangan yang baru saja terpikir oleh saya: jika Anda memiliki subclass nyata yang perlu Anda uji, Anda tidak dapat menggunakan kembali subclass tes yang Anda tulis untuk kelas induk. Anda harus membuat subclass tes untuk setiap subclass nyata, dan itu harus mengesampingkan fungsi create yang sama.
Jawaban:
Ini adalah pendekatan yang baik untuk memulai. Perhatikan bahwa yang paling penting adalah untuk menutupi kode Anda yang ada dengan tes unit; setelah Anda menjalani tes, Anda dapat melakukan refactor lebih bebas untuk meningkatkan desain Anda lebih lanjut.
Jadi poin awalnya bukan untuk membuat desain elegan dan berkilau - hanya untuk membuat unit kode diuji dengan perubahan paling tidak berisiko . Tanpa tes unit, Anda harus ekstra konservatif dan berhati-hati untuk menghindari melanggar kode Anda. Perubahan awal ini bahkan dapat membuat kode terlihat lebih canggung atau jelek dalam beberapa kasus - tetapi jika mereka memungkinkan Anda untuk menulis unit test pertama, pada akhirnya Anda akan dapat refactor ke arah desain ideal yang Anda pikirkan.
Pekerjaan mendasar untuk membaca tentang hal ini adalah Bekerja Efektif dengan Legacy Code . Ini menggambarkan trik yang Anda tunjukkan di atas dan banyak lagi, menggunakan beberapa bahasa.
sumber
Guice-people merekomendasikan penggunaan
untuk semua properti alih-alih hanya menambahkan @Inject ke definisi mereka. Ini akan memungkinkan Anda untuk memperlakukan mereka sebagai kacang normal, yang dapat Anda lakukan
new
saat menguji (dan mengatur properti secara manual) dan @Inject dalam produksi.sumber
Ketika saya dihadapkan dengan masalah jenis ini, saya akan mengidentifikasi setiap ketergantungan kelas-untuk-tes saya dan membuat konstruktor yang dilindungi yang akan memungkinkan saya untuk melewatinya. Ini secara efektif sama dengan membuat setter untuk masing-masing, tetapi saya lebih suka mendeklarasikan dependensi pada konstruktor saya sehingga saya tidak lupa untuk instantiate di masa depan.
Jadi kelas unit baru yang dapat diuji akan memiliki 2 konstruktor:
sumber
Anda mungkin ingin mempertimbangkan idiom "pengambil menciptakan" sampai Anda dapat menggunakan ketergantungan yang disuntikkan.
Sebagai contoh,
Ini akan memungkinkan Anda untuk melakukan refactor secara internal sehingga Anda dapat mereferensikan myObject Anda melalui pengambil. Anda tidak perlu menelepon
new
. Di masa mendatang, ketika semua klien dikonfigurasi untuk memungkinkan injeksi ketergantungan, maka yang harus Anda lakukan adalah menghapus kode pembuatan di satu tempat - pengambil. Setter akan memungkinkan Anda untuk menyuntikkan benda tiruan sesuai kebutuhan.Kode di atas hanya sebuah contoh dan itu tidak secara langsung mengekspos keadaan internal. Anda harus hati-hati mempertimbangkan jika pendekatan ini sesuai untuk basis kode Anda.
Saya juga menyarankan agar Anda membaca " Bekerja Efektif dengan Kode Warisan " yang berisi banyak tips berguna untuk membuat keberhasilan refactoring besar.
sumber