Stubbing Properties dengan setter pribadi untuk pengujian

10

Kami memiliki objek

public class MyObject{
    protected MyObject(){}

    public string Property1 {get;private set;}
    public string Property2 {get;private set;}
    public string Property3 {get;private set;}
    public string Property4 {get;private set;}
    public string Property5 {get;private set;}
    public string Property6 {get;private set;}
    public string Property7 {get;private set;}
    public string Property8 {get;private set;}
    public string Property9 {get;private set;}
    public string Property10 {get;private set;}
}

Dalam kode produksi kami, kami mengisi objek ini melalui automapper. Itu dapat mengakses properti dan mengaturnya dengan benar.

Sekarang ketika kami ingin menguji kelas ini di pipeline masa depan, tidak mungkin untuk mengisi properti dengan nilai dummy (untuk diuji terhadap).

Ada beberapa opsi yang tersedia.

  • Konstruktor khusus untuk menerima parameter yang diperlukan untuk pengujian dan mengatur properti, saat ini 3 konstruktor diperlukan. Ini tidak bersih karena konstruktor tidak melayani fungsionalitas bisnis apa pun.

  • Jadikan properti virtual agar kelas bisa terhenti. Tetapi menandai properti virtual tidak memberikan nilai bisnis apa pun dan mencemari kelas saya.

  • Tambahkan pembangun objek ke kelas untuk membangun objek secara internal. Sekali lagi tidak ada nilai tambah bisnis. Mungkin sedikit lebih bersih tetapi masih banyak kode yang tidak relevan dalam objek domain.

Adakah saran, saran atau opsi alternatif di sini?

JMan
sumber

Jawaban:

8

Anda memiliki sejumlah opsi.

  • Silakan dan gunakan konstruktor khusus, properti virtual, atau pembangun objek. Alasan di balik ini adalah bahwa suatu benda harus berdiri sendiri, dan tidak bergantung pada sihir seperti pembuat mobil. Kelas yang sama sekali tidak berguna kecuali ada sihir yang terjadi bukanlah pemikiran yang baik tentang kelas. "Nilai bisnis" bukan satu-satunya penentu desain yang baik.

  • Sertakan automapper dalam proses pengujian. Beberapa orang akan mengatakan ini bukan tes unit lagi. Tidak masalah. Pengujian unit bukan satu - satunya jenis pengujian.

  • Menerapkan sesuatu yang menyediakan fungsionalitas automapper khusus untuk pengujian. Anda dapat dengan mudah menulis utilitas kecil yang akan menggunakan refleksi untuk mengisi objek Anda dari kamus yang berisi nama dan nilai properti.

Lihat juga pertanyaan & jawaban ini: Apakah Anda lebih suka membuat barang pribadi internal / publik untuk pengujian, atau menggunakan semacam peretasan seperti PrivateObject?

Mike Nakis
sumber
3

Saya tidak ragu untuk menggunakan refleksi untuk hal-hal seperti ini dalam ujian.

Saya tidak suka membuat hal-hal virtual untuk mengejek karena mengubah kode karena alasan yang salah.

Saya tidak tahu automapper tapi saya setuju dengan @Mike bahwa memasukkannya dalam tes bisa menjadi ide bagus. Tes unit perbedaan / tes integrasi tidak begitu menarik. Yakin jika test suite semakin besar dan lambat, Anda perlu menyaring dan mengklasifikasikan hal-hal untuk menjalankan hanya subset yang masuk akal dari semua tes pada frekuensi tertinggi.

Contoh peretasan menggunakan refleksi, menggunakan nameof () akan memiliki performa yang lebih baik tetapi kemudian Anda kehilangan tipe .:

public static class TestExtensions
{
    public static void SetProperty<TSource, TProperty>(
        this TSource source,
        Expression<Func<TSource, TProperty>> prop,
        TProperty value)
    {
        var propertyInfo = (PropertyInfo)((MemberExpression)prop.Body).Member;
        propertyInfo.SetValue(source, value);
    }
}
Johan Larsson
sumber
0

Untuk tujuan pengujian unit, gunakan kerangka kerja mengejek seperti Microsoft Fakes, TypeMock, dan JustMock yang menyediakan dukungan untuk mengejek anggota pribadi.

Silakan lihat Smocks (paket @nuget yang tersedia) juga. Batasan Smocks adalah, itu tidak akan memberikan akses ke anggota pribadi. Tetapi memiliki kemampuan untuk mengejek anggota statis dan non-virtual. Juga tersedia secara gratis.

Cara paling sederhana lainnya adalah menggunakan, PrivateObject / PrivateType.

Karthik V
sumber