Mock framework vs frameworki MS Fakes

99

Trochę zdezorientowany różnicami między frameworkami Mock, takimi jak NMock, a VS 2011 Fakes Framework. Przechodząc przez MSDN, rozumiem, że Fakes pozwalają na wyśmiewanie zależności, tak jak RhinoMock lub NMock, jednak podejście jest inne, Fakes generuje kod, aby uzyskać tę funkcjonalność, ale framework Mocks nie. Czy więc moje rozumienie jest prawidłowe? Czy podróbki to tylko kolejny model makiety

Nairooz NIlafdeen
źródło

Odpowiedzi:

189

Twoje pytanie dotyczyło różnic między frameworkiem MS Fakes a NMock i wydaje się, że inne odpowiedzi częściowo rozwiązały ten problem, ale tutaj jest więcej informacji na temat tego, czym są takie same i czym się różnią. NMock jest również podobny do RhinoMocks i Moq, więc grupuję je w NMock.

Istnieją 3 główne różnice, które widzę od razu między NMock / RhinoMocks / Moq i MS Fakes Framework:

  • Fake framework MS używa wygenerowanego kodu, podobnie jak Accessors w poprzednich wersjach programu Visual Studio zamiast typów ogólnych. Jeśli chcesz użyć fałszywej struktury dla zależności, dodaj zestaw, który zawiera zależność, do odwołań do projektu testowego, a następnie kliknij go prawym przyciskiem myszy, aby wygenerować podwojenie testu (kody pośredniczące lub podkładki). Następnie podczas testowania w rzeczywistości używasz tych wygenerowanych klas. NMock używa generycznych do osiągnięcia tego samego (tj IStudentRepository studentRepository = mocks.NewMock<IStudentRepository>().). Moim zdaniem podejście ramowe MS Fakes utrudnia nawigację po kodzie i refaktoryzację z poziomu testów, ponieważ w rzeczywistości pracujesz z wygenerowaną klasą, a nie z prawdziwym interfejsem.

  • Fałszywe ramy MS dostarczają stubów i pieprzyków (podkładek), podczas gdy NMock, RhinoMocks i Moq wszystkie zapewniają niedopałki i makiety . Naprawdę nie rozumiem decyzji stwardnienia rozsianego, aby nie uwzględniać kpiny i osobiście nie jestem fanem pieprzyków z powodów opisanych poniżej.

  • Dzięki frameworkowi MS fałszywych możesz zapewnić alternatywną implementację metod, które chcesz zablokować. W ramach tych alternatywnych implementacji można określić zwracane wartości i śledzić informacje o tym, jak i czy metoda została wywołana. Za pomocą NMock, RhinoMocks i Moq generujesz obiekt pozorowany, a następnie używasz tego obiektu do określenia wartości zwracanych przez skrót lub do śledzenia interakcji (czy i jak zostały wywołane metody). Uważam, że podejście do podróbek MS jest bardziej złożone i mniej wyraziste.

Aby wyjaśnić różnicę w tym, co zapewniają frameworki: NMock, RhinoMocks i Moq zapewniają dwa typy testów podwójnych (stubs i mock). Fałszywe ramy zapewniają niedopałki i pieprzyki (nazywają je podkładkami) i niestety nie zawierają prób. Aby zrozumieć różnice i podobieństwa między NMock i MS Fakes, pomocne jest zrozumienie, czym są te różne typy testów podwójnych:

Stubs: Stubs są używane, gdy musisz podać wartości metod lub właściwości, o które zostanie poproszony o podwojenie testu przez testowaną metodę. Na przykład, gdy moja testowana metoda wywołuje metodę DoesStudentExist () podwójnego testu IStudentRepository, chcę, aby zwracała wartość true.

Pomysł na stuby w podróbkach NMock i MS jest taki sam, ale z NMock można zrobić coś takiego:

Stub.On(mockStudentRepository).Method("DoesStudentExist").Will(Return.Value(true));

A z MSFakes zrobiłbyś coś takiego:

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() // Generated by Fakes.
{
    DoesStudentExistInt32 = (studentId) => { return new Student(); }
};

Zwróć uwagę, że w przykładzie MS Fakes tworzysz zupełnie nową implementację metody DoesStudentExist (zwróć uwagę, że nazywa się ona DoesStudentExistInt32, ponieważ fałszywa struktura dodaje typy danych parametrów do nazw metod podczas generowania obiektów pośredniczących. Myślę, że to przesłania przejrzystość testy). Szczerze mówiąc, implementacja NMock również mnie niepokoi, ponieważ używa łańcucha do identyfikacji nazwy metody. (Wybacz mi, jeśli źle zrozumiałem, w jaki sposób ma być używany NMock.) To podejście naprawdę hamuje refaktoryzację iz tego powodu gorąco polecam RhinoMocks lub Moq zamiast NMock.

Mocks: Mocks służy do weryfikacji interakcji między testowaną metodą a jej zależnościami. W NMock możesz to zrobić, ustawiając oczekiwania podobne do tego:

Expect.Once.On(mockStudentRepository).Method("Find").With(123);

Jest to kolejny powód, dla którego wolałbym RhinoMocks i Moq od NMock, NMock używa starszego stylu oczekiwania, podczas gdy RhinoMocks i Moq obsługują podejście Arrange / Act / Assert, w którym określasz oczekiwane interakcje jako asercje na końcu takiego testu :

stubStudentRepository.AssertWasCalled( x => x.Find(123));

Ponownie zwróć uwagę, że RhinoMocks używa lambda zamiast ciągu do identyfikacji metody. Frameworka MS Fakes w ogóle nie udostępnia makiet. Oznacza to, że w twoich odgałęzionych implementacjach (zobacz opis pośredników powyżej) musisz ustawić zmienne, które później sprawdzisz, że zostały ustawione poprawnie. To wyglądałoby mniej więcej tak:

bool wasFindCalled = false;

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() 
{
    DoesStudentExistInt32 = (studentId) => 
        { 
            wasFindCalled = true;
            return new Student(); 
        }
};

classUnderTest.MethodUnderTest();

Assert.IsTrue(wasFindCalled);

Uważam, że to podejście jest trochę zawiłe, ponieważ musisz śledzić wywołanie w odgałęzieniu, a następnie potwierdzić je później w teście. Uważam, że przykłady NMock, a zwłaszcza RhinoMocks, są bardziej wyraziste.

Krety (podkładki): Szczerze mówiąc, nie lubię pieprzyków ze względu na ich możliwość niewłaściwego użycia. Jedną z rzeczy, które tak bardzo lubię w testach jednostkowych (a zwłaszcza TDD) jest to, że testowanie kodu pomaga zrozumieć, gdzie napisałeś kiepski kod. Dzieje się tak, ponieważ testowanie źle napisanego kodu jest trudne. Nie jest to prawdą w przypadku używania moli, ponieważ mole są tak naprawdę zaprojektowane, aby umożliwić testowanie pod kątem zależności, które nie są wstrzykiwane, lub testowanie metod prywatnych. Działają podobnie do stubów, z wyjątkiem tego, że używasz ShimsContext w następujący sposób:

using (ShimsContext.Create())
{
    System.Fakes.ShimDateTime.NowGet = () => { return new DateTime(fixedYear, 1, 1); };
}

Martwię się, że ludzie zaczną postrzegać je jako „łatwiejszy sposób na testowanie jednostkowe”, ponieważ nie zmusza to do pisania kodu tak, jak powinieneś. Aby uzyskać pełniejszy opis tej koncepcji, zobacz mój post:

Aby uzyskać więcej informacji na temat niektórych problemów związanych z fałszywymi ramami, zapoznaj się z tymi postami:

Jeśli jesteś zainteresowany nauką RhinoMocks, oto film szkoleniowy Pluralsight (pełne ujawnienie - napisałem ten kurs i otrzymuję tantiemy za wyświetlenia, ale myślę, że dotyczy to tej dyskusji, więc dołączam to tutaj):

Jim Cooper
źródło
14
@Jim Nie zgadzam się, że zezwolenie na „testowanie pod kątem zależności, które nie są wstrzykiwane” jest złą rzeczą. Wręcz przeciwnie, w praktyce ta umiejętność pomaga uniknąć zaśmiecania bazy kodu wieloma bezsensownymi interfejsami i związaną z nimi konfiguracją / złożonością „okablowania obiektowego”. Widziałem już kilka projektów (zarówno Java, jak i .NET), które miały setki interfejsów Java / C # z tylko jedną klasą implementującą i wiele bezużytecznych konfiguracji XML (dla fasoli Spring DI lub w Web.config dla MS Unity struktura). Lepiej nie wstrzykiwać takich zależności .
Rogério,
19
@Rogerio, pracowałem nad wieloma projektami z tysiącami klas, które podążają za tym modelem, a kiedy wstrzyknięcie zależności jest wykonane prawidłowo (jeśli używasz konfiguracji XML, nie robisz tego dobrze, imho), jest to proste i stosunkowo bezbolesne. Z drugiej strony pracowałem nad systemami, które tego nie robią, a sprzężenie między zajęciami zawsze sprawiało mi znaczny ból. Testowanie nie jest powodem używania iniekcji zależności (chociaż nie jest to zły powód). Prawdziwym powodem jest poluzowane sprzęgło. Posiadanie interfejsów zaimplementowanych przez jedną klasę nigdy nie sprawiało mi bólu.
Jim Cooper
17
@Jim, uważam możliwość testowania złego projektu za duży plus. Pierwszą rzeczą, którą chcesz zrobić przed refaktoryzacją starszego kodu w kierunku lepszego projektu, jest napisanie testów.
Thomas Materna,
4
Moja zasada jest taka, że ​​używam Fakes tylko wtedy, gdy muszę Shimować metodę współdzieloną / statyczną lub klasę, do której nie mam dostępu, aby zaimplementować interfejs. Do wszystkiego innego używam Moq. Fałszywe są wolniejsze (ponieważ fałszywe zestawy muszą zostać wygenerowane) i dopasowanie funkcji, które uzyskujesz dzięki Moq, wymaga więcej wysiłku związanego z kodowaniem.
Nick
5
@ CarloV.Dango Przede wszystkim doskonale wiem, że Dependency Injection nie wymaga oddzielnych interfejsów; mój poprzedni komentarz był tylko aluzją do tendencji do tworzenia takich interfejsów podczas korzystania z DI. Po drugie, „IOC” (Inversion of Control) nie ma nic wspólnego z DI! (Pierwsza dotyczy odwrócenia przepływu kontroli między metodami, podczas gdy druga dotyczy rozwiązania zależności między komponentem / obiektem). Myląc IOC i DI, to ty okazujesz ignorancję, a nie ja; i szczerze mówiąc, ta strona nie zasługuje na obrażanie innych ludzi, więc proszę, zachowajmy ją uprzejmie.
Rogério,
23

Masz rację, ale w tej historii jest coś więcej. Najważniejsze rzeczy, które należy wyciągnąć z tej odpowiedzi, to:

  1. Twoja architektura powinna właściwie wykorzystywać kody pośredniczące i iniekcję zależności, zamiast polegać na kuli fałszywej i fałszywej

  2. Fałszerstwa i makiety są przydatne do testowania kodu, którego nie powinieneś lub nie możesz zmieniać, na przykład:

    • Starszy kod, który nie wykorzystuje (ani efektywnie wykorzystuje) kodów pośredniczących
    • Interfejsy API innych firm
    • Zasoby, dla których nie masz kodu źródłowego

Podkładki (znane jako „Moles” podczas programowania) są rzeczywiście kpiącym frameworkiem, który działa na zasadzie wywołań okrężnych. Zamiast mozolnie budować makietę (tak, nawet użycie Moq jest stosunkowo bolesne!), Podkładki po prostu wykorzystują obiekt kodu produkcyjnego już na miejscu. Podkładki dystansowe po prostu przekierowują połączenie z miejsca docelowego produkcji do delegata testu.

Stuby są generowane z interfejsów w projekcie docelowym. Obiekt Stub jest właśnie tym - implementacją interfejsu. Zaletą używania typu Stub jest to, że możesz szybko wygenerować kod pośredniczący bez zaśmiecania projektu testowego wieloma jednorazowymi kodami pośredniczącymi, nie wspominając o marnowaniu czasu na ich tworzenie. Oczywiście nadal powinieneś tworzyć konkretne odcinki do wykorzystania w wielu testach.

Skuteczne wdrażanie podróbek (typu podkładki, makiety i odcinki) wymaga trochę przyzwyczajenia, ale jest warte wysiłku. Osobiście zaoszczędziłem tygodnie na programowaniu, używając typów podkładek / kretów, makiet i odcinków. Mam nadzieję, że będziesz się dobrze bawić z technologią tak, jak ja!

Mike Christian
źródło
11
Ocena negatywna ze względu na brak szczegółów i problemy z terminologią związane z Twoimi komentarzami na temat podróbek. Fałszywy to ogólny termin określający wszystkie typy testów podwójnych, a także nazwa MS używana dla ich biblioteki podróbek. W ich bibliotece tylko podkładki są w rzeczywistości kretami, a odcinki nie. Jeśli chodzi o potrzebę przykładów, chciałbym zobaczyć w Twojej odpowiedzi przykład, który pokazuje, jak tworzenie podkładki (lub odgałęzienia) z Fakes jest prostsze niż użycie Moq. Jeśli zmienisz swoją odpowiedź, aby rozwiązać te problemy, zmienię mój głos przeciw.
Jim Cooper
1
Ale dodaje dobrego uzasadnienia dla użycia podkładek (moli), tj. Kodu stron trzecich, API systemu / SDK. Nie chodzi tylko o to, gdy pracujesz z własnymi rozwiązaniami wewnętrznymi. Więc dam ci jeden głos, żeby to wyrównać :-)
Tony Wall
2
@Mike i Jim .. Starałem się poprawić terminologię używaną w tej odpowiedzi. Daj mi znać, jeśli możemy to ulepszyć. Dzięki.
Snesh
15

Jak rozumiem, zespół Visual Studio chciał uniknąć konkurowania z różnymi bibliotekami próbnymi dostępnymi dla .NET. SM często staje przed trudnymi decyzjami, takimi jak ta. Są przeklęci, jeśli nie zapewniają pewnych funkcji („dlaczego firma MS nie dostarcza nam fałszywej biblioteki; makiety są tak powszechnym wymaganiem?”) I przeklęci, jeśli to robią („dlaczego Microsoft działa tak agresywnie i napędza naturalnych zwolenników poza rynkiem? ”). Bardzo często, choć nie zawsze, decydują się powstrzymać się od zwykłego dostarczania własnej alternatywy dla dostępnych i dobrze przyjętych technologii. Wydaje się, że tak jest w tym przypadku.

Funkcja podkładek podrobionych jest naprawdę, bardzo przydatna. Jasne, istnieją niebezpieczeństwa. Potrzeba pewnej dyscypliny, aby upewnić się, że używasz tego tylko wtedy, gdy jest to konieczne. Jednak wypełnia dużą lukę. Moja główna skarga dotyczy tego, że jest dostarczana tylko z wersją Ultimate VS 2012 i dlatego będzie dostępna tylko dla podsekcji społeczności programistów .NET. Jaka szkoda.

Charles Young
źródło
2
Struktura podróbek jest również dostępna w edycji Premium.
AlwaysAProgrammer
13

Podróbki obejmują dwa różne rodzaje „fałszywych” przedmiotów. Pierwsza, zwana „odgałęzieniem”, jest generowaną automatycznie atrapą, której domyślne zachowanie można (i zwykle byłoby) nadpisane, aby uczynić ją bardziej interesującą. Brakuje jednak niektórych funkcji, które oferuje większość obecnie dostępnych frameworków do mockowania. Na przykład, jeśli chcesz sprawdzić, czy została wywołana metoda na instancji pośredniczącej, musisz samodzielnie dodać logikę do tego. Zasadniczo, jeśli teraz ręcznie tworzysz własne makiety, kody pośredniczące prawdopodobnie wydawałyby się ulepszeniem. Jeśli jednak używasz już bardziej w pełni funkcjonalnego frameworka do kpiny, możesz odnieść wrażenie, że brakuje kilku ważnych elementów w fałszywych odcinkach.

Druga kategoria obiektów oferowana przez Fakes, zwana "shim", ujawnia mechanizm zastępowania zachowania zależności, które nie zostały (lub nie mogą być) odpowiednio odsprzężone w celu standardowej wymiany za pomocą makiet. AFAIK, TypeMock jest jedyną z głównych platform do makietowania, która obecnie oferuje tego rodzaju funkcje.

BTW, jeśli wypróbowałeś wcześniej Moles, Fakes jest w zasadzie tym samym, ostatecznie wydostając się z Microsoft Research i do rzeczywistego produktu.

Nicole Calinoiu
źródło
1
Aktualizacja: Mole zostały teraz zintegrowane z MS Fakes. „Fake Framework w programie Visual Studio 2012 to następna generacja Moli i Stubów. Fałszywe różnią się jednak od Moli, więc przejście z Moli na Fakes będzie wymagało pewnych modyfikacji w kodzie. Framework Moles nie będzie obsługiwany w Visual Studio 2012 . ” Źródło: research.microsoft.com/en-us/projects/moles
Ojciec
1

Jeśli chodzi o obiekty Fake (Shim + Stub), zostało to dobrze zdefiniowane powyżej, chociaż myślę, że ostatni akapit w ostatnim komentarzu podsumowuje całą sytuację całkiem dobrze.

Chociaż wiele osób będzie argumentować, że obiekty Fake (Shim + Stub) są dobrymi zasobami, które można mieć w niektórych przypadkach testów jednostkowych, wadą jest to, że niezależnie od tego, czy używasz programu Visual Studio 2012, czy Visual Studio 2013, te opcje są dostępne TYLKO z wersjami Premium lub Ultimate. IOW, oznacza to, że NIE BĘDZIE używać ŻADNEGO z tych podróbek (Shim + Stub) na żadnej wersji Pro.

Prawdopodobnie zobaczysz opcję menu Fakes (Shim + Stub) w wersjach Pro, ale uważaj, są dość duże szanse, że skończysz z absolutnie niczym ... Nie wygeneruje to żadnego błędu kompilacji, który powie ci, że coś ważnego brakuje, opcji po prostu nie ma, więc nie trać czasu ...

Jest to ważny czynnik do rozważenia w zespole deweloperskim, zwłaszcza jeśli tylko jeden używa wersji Ultimate, podczas gdy wszyscy inni używają wersji Pro ... Z drugiej strony Moq można łatwo zainstalować za pośrednictwem Nuget bez względu na używaną wersję Visual Studio. Nie miałem problemu z Moq, kluczem do każdego narzędzia jest wiedzieć, do czego są używane i jak z nich korzystać;)

SirXamelot
źródło
1
Sformatuj pytanie w mniejsze akapity, aby było łatwiejsze do odczytania.
@SirXamelot, bez refaktoryzacji kodu nie można zmienić danych wyjściowych wywołań statycznych podanych przez .net, np. DateTime.Now lub Guid.NewGuid
zaitsman