Mocking objek dengan Moq ketika konstruktor memiliki parameter

94

Saya memiliki objek yang saya coba tiru menggunakan moq. Konstruktor objek memiliki parameter yang diperlukan:

public class CustomerSyncEngine {
    public CustomerSyncEngine(ILoggingProvider loggingProvider, 
                              ICrmProvider crmProvider, 
                              ICacheProvider cacheProvider) { ... }
}

Sekarang saya mencoba membuat tiruan untuk objek ini menggunakan sintaks "setup" moq v3 atau v4 "Mock.Of" tetapi tidak dapat menemukan jawabannya ... semua yang saya coba tidak memvalidasi. Inilah yang saya miliki sejauh ini, tetapi baris terakhir memberi saya objek nyata, bukan tiruan. Alasan saya melakukan ini adalah karena saya memiliki metode di CustomerSyncEngine yang ingin saya verifikasi dipanggil ...

// setup
var mockCrm = Mock.Of<ICrmProvider>(x => x.GetPickLists() == crmPickLists);
var mockCache = Mock.Of<ICacheProvider>(x => x.GetPickLists() == cachePickLists);
var mockLogger = Mock.Of<ILoggingProvider>();

// need to mock the following, not create a real class like this...
var syncEngine = new CustomerSyncEngine(mockLogger, mockCrm, mockCache);
Andrew Connell
sumber
Dapatkah Anda memberikan metode sampel yang ingin Anda verifikasi panggilannya?
Ciaran
4
Jadi jika saya memiliki ketergantungan pada Kelas daripada Antarmuka saya harus mengejek bahkan ketergantungan mereka, ini turun secara rekursif. Pada akhirnya saya terpaksa menggunakan beberapa antarmuka untuk menjaga kode saya dapat diuji, bahkan jika saya tidak memerlukan antarmuka dalam kode saya. Saya pikir terlalu banyak antarmuka adalah bau yang lebih besar daripada mengejek kelas beton ...
Tarion

Jawaban:

34

Baris terakhir memberi Anda contoh nyata karena Anda menggunakan kata kunci baru, tidak mengejek CustomerSyncEngine.

Kamu harus menggunakan Mock.Of<CustomerSyncEngine>()

Satu-satunya masalah dengan jenis Beton Mocking adalah bahwa Moq akan memerlukan konstruktor default publik (tanpa parameter) ATAU Anda perlu membuat Moq dengan spesifikasi arg konstruktor. http://www.mockobjects.com/2007/04/test-smell-mocking-concrete-classes.html

Hal terbaik untuk dilakukan adalah mengklik kanan pada kelas Anda dan memilih antarmuka Ekstrak.

Raghu
sumber
3
Berkenaan dengan masalah tersebut, alternatifnya adalah dengan menggunakan wadah AutoMocking. Favorit saya adalah Machine.Fakes dalam hubungannya dengan Machine.Spesifikasi, menggunakan wadah penguncian otomatis membuatnya lebih mudah untuk menguji area permukaan yang lebih kecil. Misalkan Andrew perlu menguji metode CustomerSyncEngineyang hanya digunakan ICrmProviderdengan implementasi tiruan tradisional harus disediakan untuk ketiga antarmuka sedangkan wadah penguncian otomatis akan memungkinkan Anda untuk hanya menyediakan satu.
Chris Marisic
74

Ubah baris terakhir menjadi

var syncEngine = new Mock<CustomerSyncEngine>(mockLogger, mockCrm, mockCache).Object;

dan itu harus berhasil

Suhas
sumber
3
Tidak yakin bagaimana komentar ini berlaku untuk jawaban saya?
Suhas
2
Karena ini akan menyebabkan kesalahan kompilasi karena mockLogger dan yang lainnya akan mengeluarkan pengecualian bahwa mereka tidak memiliki properti Object
Justin Pihony
2
Karena OP menggunakan Mock.Of <T> () untuk membuat tiruan jenis logger, crm, dan cache, objek yang dikembalikan dikembalikan sebagai T, bukan sebagai Mock <T>. Jadi, mockLogger.Object dll. Tidak diperlukan saat memberikannya ke Mock of CustomerSyncEngine dan seperti yang disebutkan @JustinPihony, akan menunjukkan kesalahan waktu desain kepada Anda.
Josh Gust
1
@suhas Shouldn't his benew Mock<CustomerSyncEngine>(new object[]{mockLogger, mockCrm, mockCache}).Object;
GiriB
@GiriB tidak diperlukan, tapi mungkin, karena tiruan didefinisikan dengan Params. public Mock (params object [] args)
Jiří Herník