Deridere gli oggetti con Moq quando il costruttore ha parametri


92

Ho un oggetto che sto cercando di imitare usando moq. Il costruttore dell'oggetto ha parametri richiesti:

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

Ora sto cercando di creare il mock per questo oggetto usando la sintassi v3 "setup" o v4 "Mock.Of" di moq ma non riesco a capirlo ... tutto quello che sto provando non si convalida. Ecco quello che ho finora, ma l'ultima riga mi sta dando un oggetto reale, non il mock. Il motivo per cui lo sto facendo è perché ho metodi su CustomerSyncEngine che voglio verificare vengano chiamati ...

// 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);

Potete fornire un metodo di esempio che desiderate verificare di essere chiamato?
Ciaran

4
Quindi, se ho dipendenze da classi piuttosto che da interfacce, devo deridere anche le loro dipendenze, questo va giù ricorsivamente. Alla fine sono costretto a usare alcune interfacce per mantenere il mio codice testabile, anche se non ho bisogno delle interfacce nel mio codice. Penso che troppe interfacce siano un odore più grande di
prendere in

Risposte:


34

L'ultima riga ti dà un'istanza reale perché stai usando la nuova parola chiave, non deridendo CustomerSyncEngine.

Dovresti usare Mock.Of<CustomerSyncEngine>()

L'unico problema con i tipi Mocking Concrete è che Moq avrebbe bisogno di un costruttore predefinito pubblico (senza parametri) OPPURE è necessario creare il Moq con la specifica arg del costruttore. http://www.mockobjects.com/2007/04/test-smell-mocking-concrete-classes.html

La cosa migliore da fare sarebbe fare clic con il tasto destro sulla classe e scegliere Estrai interfaccia.


3
Per quanto riguarda il problema, un'alternativa è utilizzare un contenitore AutoMocking. Il mio preferito è Machine.Fakes in combinazione con Machine.Specifications, l'utilizzo di un contenitore autobloccante rende più facile testare aree di superficie più piccole. Supponiamo che Andrew abbia bisogno di testare un metodo in CustomerSyncEnginequanto ICrmProviderdeve essere fornito solo l'uso con implementazioni di mocking tradizionali per tutte e 3 le interfacce, mentre un contenitore di automocking consentirebbe di fornirne solo uno.
Chris Marisic

73

Cambia l'ultima riga in

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

e dovrebbe funzionare


3
Non sei sicuro di come questo commento si applichi alla mia risposta?
Suhas

2
Perché ciò causerebbe un errore di compilazione in quanto mockLogger e altri genereranno un'eccezione che non hanno una proprietà Object
Justin Pihony

2
Poiché l'OP utilizza Mock.Of <T> () per creare mock dei tipi logger, crm e cache, l'oggetto restituito viene restituito come T, non come Mock <T>. Quindi, mockLogger.Object ecc. Non è necessario quando li si fornisce al Mock di CustomerSyncEngine e, come menzionato da @JustinPihony, dovrebbe mostrare un errore in fase di progettazione.
Josh Gust

1
@suhas non dovrebbe essere il suonew Mock<CustomerSyncEngine>(new object[]{mockLogger, mockCrm, mockCache}).Object;
GiriB

@GiriB non è necessario, ma possibile, poiché il mock è definito con Params. public Mock (params object [] args)
Jiří Herník il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.