Bagaimana cara menyuntikkan benda uji saat benda nyata dibuat secara dinamis?

8

Saya ingin membuat kelas diuji menggunakan injeksi ketergantungan. Tetapi kelas membuat beberapa objek saat runtime, dan meneruskan nilai yang berbeda ke konstruktornya. Ini contoh sederhana:

public abstract class Validator {
    private ErrorList errors;

    public abstract void validate();

    public void addError(String text) {
        errors.add(
            new ValidationError(text));
    }

    public int getNumErrors() {
        return errors.count()
    }
}

public class AgeValidator extends Validator {
    public void validate() {
        addError("first name invalid");
        addError("last name invalid");
    }
}

(Ada banyak subclass Validator lainnya.)

Apa cara terbaik untuk mengubah ini, jadi saya bisa menyuntikkan objek palsu alih-alih ValidationError?

Saya dapat membuat AbstractValidationErrorFactory, dan menyuntikkan pabrik sebagai gantinya. Ini akan berhasil, tetapi sepertinya saya akhirnya akan menciptakan banyak pabrik kecil dan antarmuka pabrik, untuk setiap ketergantungan seperti ini. Apakah ada cara yang lebih baik?

JW01
sumber
Apa yang ingin Anda uji? Bahwa Validator menciptakan ValidationErrors? Memalsukan ValidasiError tidak akan membantu. Sebagai kode berdiri, Validator tidak terlalu berguna karena Anda dapat memanggil validasi, tetapi Anda tidak bisa mendapatkan kesalahan yang dihasilkan.
Michael Brown
Saya meninggalkannya, tetapi ada metode lain yang menyediakan akses ke objek ErrorList. Jadi saya bisa tes menggunakan itu.
JW01
Lihat stackoverflow.com/questions/4859523/… untuk info terkait
blueberryfields

Jawaban:

4

Perubahan paling sederhana untuk membuat unit kelas Anda dapat diuji adalah dengan

  1. (ekstrak kode pembuatan ke metode baru (virtual) - Anda sudah memiliki ini)
  2. subkelas kelas yang diuji, menimpa metode pembuatan untuk membuat objek tiruan yang sesuai, bukan yang asli
  3. tulis tes unit Anda :-)

Tentu saja, ini tidak terlalu baik. Tetapi ini memungkinkan Anda untuk menutup kelas dengan unit test, setelah itu Anda dapat mulai refactoring ke arah desain yang ideal, tanpa takut merusak kode dengan perubahan yang lebih dalam.

Referensi wajib: Bekerja Efektif dengan Legacy Code .

Tentang desain yang ideal itu sulit untuk mengatakan banyak, karena contoh Anda kurang detail dan konteks. Jika Anda memberi tahu kami lebih banyak tentang apa yang ingin Anda uji dan apa peran kelas yang diuji, kami mungkin dapat membantu lebih banyak.

Péter Török
sumber
Itu sudah pendekatan umum saya, tetapi dalam kasus ini ada beberapa subclass Validator, dan ada banyak pengaturan yang terlibat dalam pengujian. Jika saya membuat subclass uji, maka saya juga harus membuat subclass uji untuk setiap subclass nyata dari Validator. Jadi saya akhirnya akan menduplikasi banyak pekerjaan setup saya. Tapi mungkin itu perlu dalam kasus ini.
JW01
2

Biasanya solusi untuk masalah ini adalah pola Provider:

public class Validator {
    public Provider<ValidationError> errorProvider;

    public void addError(String text) {
        errors.add(errorProvider.get(text));
    }
}

Penyedia adalah sesuatu yang mirip dengan pabrik. Dalam hal ini akan kembali baru ValidationErrorsetiap kali getdipanggil.

Informasi lebih lanjut dapat ditemukan di buku Injeksi Ketergantungan di bagian 3.3.2 (Reinjeksi dengan pola Penyedia).

Guice , misalnya, memiliki Penyedia untuk ini. Jika Anda menginginkan sesuatu yang lebih canggih, Anda dapat menggunakan AssistedInject .

Malaikat mudah
sumber