Mockowanie obiektów za pomocą Moq, gdy konstruktor ma parametry


94

Mam obiekt, który próbuję udawać, używając moq. Konstruktor obiektu ma wymagane parametry:

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

Teraz próbuję utworzyć makietę dla tego obiektu przy użyciu składni moq v3 „setup” lub v4 „Mock.Of”, ale nie mogę tego rozgryźć ... wszystko, co próbuję, nie jest sprawdzane. Oto, co mam do tej pory, ale ostatnia linijka daje mi prawdziwy przedmiot, a nie próbę. Powodem, dla którego to robię, jest to, że mam metody w CustomerSyncEngine, które chcę sprawdzić, są wywoływane ...

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

Czy możesz podać przykładową metodę, którą chcesz zweryfikować?
Ciaran

4
Więc jeśli mam zależności od klas, a nie interfejsów, muszę kpić nawet z ich zależności, to spada rekurencyjnie. Ostatecznie jestem zmuszony użyć niektórych interfejsów, aby mój kod był testowalny, nawet jeśli nie potrzebuję interfejsów w moim kodzie. Myślę, że zbyt wiele interfejsów to większy zapach niż
kpienie z

Odpowiedzi:


34

Ostatnia linia daje ci prawdziwą instancję, ponieważ używasz nowego słowa kluczowego, a nie kpiny z CustomerSyncEngine.

Powinieneś użyć Mock.Of<CustomerSyncEngine>()

Jedynym problemem z typami Mocking Concrete jest to, że Moq potrzebowałby publicznego konstruktora domyślnego (bez parametrów) LUB musisz utworzyć Moq ze specyfikacją arg konstruktora. http://www.mockobjects.com/2007/04/test-smell-mocking-concrete-classes.html

Najlepszą rzeczą do zrobienia jest kliknięcie klasy prawym przyciskiem myszy i wybranie interfejsu Extract.


3
Jeśli chodzi o problem, alternatywą jest użycie kontenera AutoMocking. Moim ulubionym jest Machine.Fakes w połączeniu ze specyfikacjami Machine.Specyfikacje, użycie pojemnika do automatycznego zamykania ułatwia testowanie mniejszych powierzchni. Załóżmy, że Andrew musiałby przetestować metodę w CustomerSyncEnginetaki sposób, że ICrmProviderdla wszystkich trzech interfejsów muszą być dostępne tylko zastosowania z tradycyjnymi implementacjami mockowania, podczas gdy kontener autmockujący pozwoliłby na udostępnienie tylko jednego.
Chris Marisic,

74

Zmień ostatnią linię na

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

i powinno działać


3
Nie wiesz, jak ten komentarz odnosi się do mojej odpowiedzi?
Suhas

2
Ponieważ spowodowałoby to błąd kompilacji, ponieważ mockLogger i inni rzucą wyjątek, że nie mają właściwości Object
Justin Pihony

2
Ponieważ OP używa Mock.Of <T> () do tworzenia mocków typów logger, crm i cache, zwracany obiekt jest zwracany jako T, a nie jako Mock <T>. Tak więc mockLogger.Object itp. Nie jest potrzebny podczas przekazywania ich do Mock of CustomerSyncEngine i, jak wspomniał @JustinPihony, powinien pokazać błąd czasu projektowania.
Josh Gust,

1
@suhas Nie powinno byćnew Mock<CustomerSyncEngine>(new object[]{mockLogger, mockCrm, mockCache}).Object;
GiriB

@GiriB nie jest potrzebne, ale możliwe, ponieważ makieta jest zdefiniowana za pomocą Params. public Mock (params object [] args)
Jiří Herník
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.