Jaka jest różnica między makietą a skrótem?


962

Czytałem różne artykuły o kpinach i karczowaniu w testach, w tym kpiny Martina Fowlera , ale nie rozumiem różnicy.



75
@OP Ponieważ nie ma różnicy. Ten artykuł, tak jak lubiany przez społeczność, czyni - z całym szacunkiem - wszystko niepotrzebnie zagmatwa, dodając dodatkowe znaczenie słowom, które łatwo zrozumieć inaczej i czyniąc sprawy niepotrzebnymi skomplikowanymi. Mock to tylko próbka, coś, co działa w oparciu o fałszywą logikę biznesową zamiast prawdziwej. Ostatecznie wybór należy do zachowania, ale nadal jest to kpina. Lub jakkolwiek chcesz to nazwać, ale uczyń to JEDEN. Nie dziel włosów. Uprość to, aby ludzie mogli łatwo zrozumieć Twoją koncepcję - z którą powyższy artykuł się nie udaje.
wst

10
„Klasyfikacja między próbkami, podróbkami i skrótami jest w literaturze wysoce niespójna”. Z wieloma cytatami. Nadal jeden z moich ulubionych cytatów z Wikipedii - jeśli coś takiego istnieje :) en.wikipedia.org/wiki/Mock_object
JD.

11
że artykuł Martina Fowlera jest naprawdę trudny do zrozumienia dla początkujących.
lmiguelvargasf

1
Rozumiem, że kod pośredniczący byłby po prostu obiektem wyrzucanym do testu, jak zbiór fałszywych danych. Mock byłby sprytnie przesłoniętą wersją czegoś bardziej złożonego, na przykład warstwy usług z różnymi metodami, których zachowanie mogło zostać zmienione w testach. Te dwie rzeczy są używane razem, tak jakbyś mógł przekazać niektóre zaległe obiekty do wyśmiewanej warstwy.
JsonStatham

Odpowiedzi:


746

Kikut

Uważam, że największym rozróżnieniem jest to, że napisałeś już odcinek ze z góry określonym zachowaniem. Tak więc miałbyś klasę, która implementuje zależność (najprawdopodobniej klasę abstrakcyjną lub interfejs), którą sfałszujesz do celów testowych, a metody zostałyby po prostu zgaszone z ustalonymi odpowiedziami. Nie zrobiliby nic wymyślnego, a ty już napisałeś dla niego kod pośredni poza testem.

Drwić

Kpina jest czymś, co w ramach testu musisz skonfigurować zgodnie ze swoimi oczekiwaniami. Makieta nie jest konfigurowana w określony sposób, więc masz kod, który robi to w teście. Próbki są w pewien sposób określane w czasie wykonywania, ponieważ kod określający oczekiwania musi zostać uruchomiony, zanim cokolwiek zrobi.

Różnica między Mockami a Stubami

Testy pisane próbnymi initialize -> set expectations -> exercise -> verifypróbami zwykle przebiegają według wzoru. Podczas gdy wstępnie napisany kod będzie następował po initialize -> exercise -> verify.

Podobieństwo między próbkami i skrótami

Celem obu jest wyeliminowanie testowania wszystkich zależności klasy lub funkcji, aby twoje testy były bardziej skoncentrowane i prostsze w tym, co próbują udowodnić.


876

Przedmowa

Istnieje kilka definicji obiektów, które nie są rzeczywiste. Ogólny termin to test podwójny . Termin ten obejmuje: manekin , fałszywy , skrótowy , próbny .

Odniesienie

Zgodnie z artykułem Martina Fowlera :

  • Atrapy są przekazywane, ale nigdy nie są używane. Zwykle służą one tylko do wypełniania list parametrów.
  • Fałszywe obiekty faktycznie mają działające implementacje, ale zwykle wymagają skrótu, który sprawia, że ​​nie nadają się do produkcji (dobrym przykładem jest baza danych w pamięci).
  • Stuby zapewniają konserwowane odpowiedzi na wywołania wykonane podczas testu, zwykle nie reagując wcale na nic poza tym, co jest zaprogramowane na test. Stuby mogą również rejestrować informacje o połączeniach, takie jak kod pośredniczący bramy e-mail, który zapamiętuje wiadomości, które „wysłał”, a może tylko liczbę wiadomości, które „wysłał”.
  • Mocks są tym, co mówimy o tutaj: Przedmioty z zaprogramowanymi oczekiwań stanowiących specyfikację połączeń oczekuje oni otrzymać.

Styl

Egzaminy próbne a stadniny = testy behawioralne a testy stanowe

Zasada

Zgodnie z zasadą testu tylko jedna rzecz na test , w jednym teście może znajdować się kilka kodów pośredniczących, ale generalnie jest tylko jedna próbka.

Koło życia

Testuj cykl życia za pomocą kodów pośredniczących:

  1. Setup - Przygotuj testowany obiekt i jego współpracowników.
  2. Ćwiczenie - przetestuj funkcjonalność.
  3. Weryfikuj stan - Użyj zapewnia, aby sprawdzić stan obiektu.
  4. Porzucenie - Oczyść zasoby.

Cykl życia testu z próbami:

  1. Dane konfiguracji - przygotuj testowany obiekt.
  2. Oczekiwania dotyczące konfiguracji - Przygotuj oczekiwania w makiecie używanej przez obiekt główny.
  3. Ćwiczenie - przetestuj funkcjonalność.
  4. Zweryfikuj oczekiwania - Sprawdź, czy w próbnej próbie wywołano prawidłowe metody.
  5. Weryfikuj stan - Użyj zapewnia, aby sprawdzić stan obiektu.
  6. Porzucenie - Oczyść zasoby.

Podsumowanie

Testy próbne i pośredniczące dają odpowiedź na pytanie: Jaki jest wynik?

Testy z próbami są również zainteresowane: Jak uzyskano wynik?


Czekaj, czy kpiny również zwracają odpowiedzi w puszkach? Bo inaczej, dlaczego odpowiadają na pytanie?
AturSams,

Z tego, co napisałeś, mogę powiedzieć, że mocks = kody pośredniczące + oczekiwania i weryfikacje, ponieważ symulacje „zapewniają konserwowane odpowiedzi na wywołania wykonane podczas testu, zwykle nie reagując wcale na nic poza tym, co jest zaprogramowane na test” (tak samo jak kody pośredniczące). A przykład, który Fowler pokazał jako przykład odcinka, jest tak naprawdę przykładem szpiega! Oznacza to, że makieta to odcinek, a szpieg to odcinek. A skrót to po prostu obiekt, który ma kilka metod działania. To wyjaśnia także, dlaczego metoda stub () przestarzała Mockito.
kolobok

To, co wydaje mi się mylące w tym i przyjętej odpowiedzi, to „ustawienie oczekiwania”, co to w ogóle oznacza? Zwykle w „głównym kodzie” tworzysz oczekiwane wyniki. Wygląda na to, że w jakiś sposób włożyłeś oczekiwania w fałszywy obiekt, co nie ma dla mnie sensu. RÓWNIEŻ, możesz równie łatwo ćwiczyć próbę z pewnym wkładem, przechowywać wynik, tworzyć „oczekiwania” później, a następnie porównywać. Używasz terminologii, którą uważam za zbyt abstrakcyjną i niejednoznaczną.
IceFire

365

Kikut to prosty fałszywy obiekt. Po prostu upewnia się, że test działa płynnie.
Makieta to mądrzejszy skrót. Sprawdzasz, czy test przechodzi przez to.


33
Myślę, że jest to najbardziej zwięzłe i trafne w odpowiedzi. Na wynos: próbny odcinek IS-A. stackoverflow.com/a/17810004/2288628 jest dłuższą wersją tej odpowiedzi.
PoweredByRice

8
Nie sądzę, żeby makieta była kikutem. Próbki służą do potwierdzenia i nigdy nie powinny zwracać danych, kody pośredniczące są używane do zwracania danych i nigdy nie powinny potwierdzać.
dave1010

2
@ dave1010 Makiety zdecydowanie mogą zwrócić dane, a nawet zgłosić wyjątek. Powinni to zrobić w odpowiedzi na przekazane im parametry.
Trenton,

2
@trenton, jeśli obiekt zwraca lub rzuca na podstawie przekazanych danych, jest to fałszywy , a nie próbny. Testy sprawdzają, w jaki sposób SUT obsługuje odbieranie wiadomości, symulują testowanie, w jaki sposób SUT wysyła wiadomości. Pomieszanie dwóch prawdopodobnie doprowadzi do złego projektu OO.
dave1010

8
Myślę, że to świetnie - skrót zwraca odpowiedzi na pytania. Próbka zwraca również odpowiedzi na pytania (jest to skrót), ale weryfikuje również, czy pytanie zostało zadane !!
Leif

238

Oto opis każdego z nich wraz z próbką ze świata rzeczywistego.

  • Atrapa - po prostu fałszywe wartości, aby spełnić API.

    Przykład : Jeśli testujesz metodę klasy, która wymaga wielu obowiązkowych parametrów w konstruktorze, które nie mają wpływu na twój test, możesz utworzyć fikcyjne obiekty w celu utworzenia nowych instancji klasy.

  • Fake - utwórz testową implementację klasy, która może zależeć od jakiejś zewnętrznej infrastruktury. (Dobrą praktyką jest to, że test jednostkowy NIE wchodzi w interakcje z zewnętrzną infrastrukturą).

    Przykład : Utwórz fałszywą implementację dostępu do bazy danych, zastąp ją in-memorykolekcją.

  • Stub - zastępuj metody zwracające wartości zakodowane, zwane także state-based.

    Przykład : Twoja klasa testowa zależy od metody Calculate()trwającej 5 minut. Zamiast czekać 5 minut, możesz zastąpić jego prawdziwą implementację kodem pośredniczącym, który zwraca zakodowane wartości; zajmuje tylko niewielką część czasu.

  • Mock - bardzo podobny, Stubale interaction-basedraczej niż oparty na stanie. Oznacza to, że nie oczekujesz, Mockże zwróci jakąś wartość, ale założysz, że tworzona jest określona kolejność wywołań metod.

    Przykład: Testujesz klasę rejestracji użytkownika. Po zadzwonieniu Savepowinien zadzwonić SendConfirmationEmail.

Stubsi Mockssą w rzeczywistości podtypami Mock, oba zamieniają rzeczywistą implementację na implementację testową, ale z różnych, konkretnych powodów.


175

W kursieodeschool.com , Rails Testing for Zombies , podają następującą definicję terminów:

Kikut

Do zamiany metody na kod, który zwraca określony wynik.

Drwić

Kod pośredniczący z twierdzeniem, że metoda jest wywoływana.

Tak więc, jak Sean Copenhaver opisał w swojej odpowiedzi, różnica polega na tym, że drwiny ustalają oczekiwania (tj. Robią twierdzenia na temat tego, czy i jak zostaną wywołane).


Aby uzupełnić post Dillona, ​​pomyśl o tym, masz klasę o nazwie „MakeACake”, która bierze kilka bibliotek: mleko, jajka, cukier, piekarnik.
aarkerio

139

Stuby nie zawodzą twoich testów, próbuj.


2
I myślę, że to dobrze, wiesz, jeśli testy mają takie samo zachowanie po refaktoryzacji.
RodriKing

1
@RodriKing Mam takie same odczucia. Podobnie jak w przypadku Mocka, z wszelkimi zmianami w kodzie produkcyjnym - wprowadzono odpowiednie zmiany w kodzie testowym. Co jest bólem! Dzięki Stubs masz wrażenie, jakbyś ciągle testował zachowanie, więc nie trzeba wprowadzać żadnych zmian w kodzie testowym.
tucq88

35

Myślę, że najprostszą i jaśniejszą odpowiedź na to pytanie udziela Roy Osherove w swojej książce The Art of Unit Testing (strona 85)

Najłatwiejszym sposobem na stwierdzenie, że mamy do czynienia z kodem pośredniczącym, jest zauważenie, że kod ten nigdy nie może zawieść testu. Twierdzi, że zastosowania testowe są zawsze w stosunku do testowanej klasy.

Z drugiej strony test użyje próbnego obiektu do sprawdzenia, czy test się nie powiódł. [...]

Ponownie, próbny obiekt to obiekt, którego używamy do sprawdzania, czy test się nie powiódł.

Oznacza to, że jeśli robisz twierdzenia przeciwko fałszywemu, oznacza to, że używasz fałszywego jako makiety, jeśli używasz fałszywego tylko w celu uruchomienia testu bez potwierdzenia, używasz fałszywego jako kodu pośredniczącego.


2
Chciałbym, żeby twoja odpowiedź znalazła się na szczycie. Oto R. Osherove wyjaśniający ten youtu.be/fAb_OnooCsQ?t=1006 .
Michael Ekoka,

31

Czytając wszystkie powyższe wyjaśnienia, postaram się zagęścić:

  • Stub : sztuczny fragment kodu, który pozwala na uruchomienie testu, ale nie obchodzi Cię, co się z nim stanie.
  • Mock : fałszywy fragment kodu, który WERYFIKUJESZ jest poprawnie nazywany w ramach testu.
  • Szpieg : sztuczny fragment kodu, który przechwytuje niektóre wywołania prawdziwego fragmentu kodu, umożliwiając weryfikację połączeń bez zastępowania całego oryginalnego obiektu.

4
Dobra odpowiedź. Mock brzmi dość podobnie do Szpiega, w oparciu o twoją definicję. Byłoby miło, gdybyś zaktualizował swoją odpowiedź, dodając jeszcze kilka podwójnych testów.
Rowan Gontier

Nie słyszałem o Szpiegu, kiedy pisałem tę odpowiedź.
O'Rooney,

23

Mock to po prostu testowanie zachowania, upewniające się, że wywoływane są określone metody. Stub to wersja testowa (per se) konkretnego obiektu.

Co masz na myśli po Apple?


19
„Co masz na myśli po Apple?” Użyj Helvetica
kubi

7
W sposób Apple, a nie w sposób Microsoft :)
never_had_a_name

2
Czy to pomaga w tej sytuacji?
NebulaFox,

21

Jeśli porównasz to do debugowania:

Stub przypomina upewnienie się, że metoda zwraca poprawną wartość

Mock jest jak wejście do metody i upewnienie się, że wszystko w środku jest poprawne przed zwróceniem poprawnej wartości.


20

Użycie modelu mentalnego naprawdę pomogło mi to zrozumieć, zamiast wszystkich wyjaśnień i artykułów, które nie do końca się „zagłębiły”.

Wyobraź sobie, że twoje dziecko ma szklany talerz na stole i zaczyna się nim bawić. Teraz boisz się, że się zepsuje. Dajesz mu zamiast tego plastikową płytkę. To byłby Mock (to samo zachowanie, ten sam interfejs, „bardziej miękka” implementacja).

Teraz powiedz, że nie masz plastikowego zamiennika, więc wyjaśnisz „Jeśli będziesz dalej z nim bawił, pęknie!”. To jest Stub , wcześniej podałeś wcześniej zdefiniowany stan.

Dummy byłoby widelec nawet nie używać ... I Spy może być coś podobnego zapewniając taką samą wyjaśnienie już używany, który pracował.


19

Myślę, że najważniejszą różnicą między nimi są ich intencje.

Pozwól, że spróbuję wyjaśnić to w DLACZEGO pośredniku kontra DLACZEGO udawanie

Załóżmy, że piszę kod testowy dla publicznego kontrolera osi czasu mojego klienta Mac twitter

Oto przykładowy kod testowy

twitter_api.stub(:public_timeline).and_return(public_timeline_array)
client_ui.should_receive(:insert_timeline_above).with(public_timeline_array)
controller.refresh_public_timeline
  • STUB: Połączenie sieciowe z interfejsem API Twittera jest bardzo wolne, przez co mój test jest powolny. Wiem, że zwróci osie czasu, więc stworzyłem stub symulujący interfejs API Twittera HTTP, dzięki czemu mój test uruchomi go bardzo szybko i mogę go uruchomić nawet w trybie offline.
  • MOCK: Nie napisałem jeszcze żadnej metody interfejsu użytkownika i nie jestem pewien, jakie metody muszę napisać dla mojego obiektu interfejsu użytkownika. Mam nadzieję wiedzieć, jak mój kontroler będzie współpracować z moim obiektem interfejsu użytkownika, pisząc kod testowy.

Pisząc próbną, odkrywasz relację współpracy między obiektami, sprawdzając, czy spełnione są oczekiwania, a kod pośredni symuluje tylko zachowanie obiektu.

Proponuję przeczytać ten artykuł, jeśli chcesz dowiedzieć się więcej o próbach: http://jmock.org/oopsla2004.pdf


1
Myślę, że masz dobry pomysł, ale Dillon Kearns wyjaśnił to znacznie jaśniej.
O'Rooney,

19

Aby być bardzo jasnym i praktycznym:

Stub: klasa lub obiekt, który implementuje metody sfałszowanej klasy / obiektu i zwraca zawsze to, co chcesz.

Przykład w JavaScript:

var Stub = {
   method_a: function(param_a, param_b){
      return 'This is an static result';
   }
}

Mock: To samo z kodem pośredniczącym, ale dodaje pewną logikę, która „weryfikuje”, gdy wywoływana jest metoda, dzięki czemu masz pewność, że niektóre implementacje wywołują tę metodę.

Jak mówi @mLevan, wyobraź sobie jako przykład, że testujesz klasę rejestracji użytkownika. Po wywołaniu Save powinien wywołać SendConfirmationEmail.

Bardzo głupi kod Przykład:

var Mock = {
   calls: {
      method_a: 0
   }

   method_a: function(param_a, param_b){
     this.method_a++; 
     console.log('Mock.method_a its been called!');
   }
}

16

Ten slajd bardzo dobrze wyjaśnia główne różnice.

wprowadź opis zdjęcia tutaj

* Z CSE 403 Wykład 16, University of Washington (slajd stworzony przez „Marty Stepp”)


To jest bardziej jasne wyjaśnienie różnicy między nimi, IMO. Dla kodu pośredniczącego: tester bierze Stub i używa go bezpośrednio w testowanej klasie. Ale w przypadku Mocka tester musi określić sposób użycia obiektu Mock. W różnych przypadkach będzie zachowywać się inaczej. W przeciwieństwie do tego, kod pośredniczący nie powinien zachowywać się inaczej, ale jest używany w takiej postaci, w jakiej jest (co oznacza zwracanie tych samych danych za każdym razem, gdy się z nim skontaktuje)
Dexter,

12

Podoba mi się wyjaśnienie Roy Osherove [link do filmu] .

Każda utworzona klasa lub obiekt jest fałszywy. Jest to próbka, jeśli zweryfikujesz przeciwko niej połączenia. W przeciwnym razie jest to zalążek.


12
  • Stubs vs. Mocks
    • Stuby
      1. podać konkretne odpowiedzi na wywołania metod
        • np .: myStubbedService.getValues ​​() po prostu zwraca ciąg znaków wymagany przez testowany kod
      2. używane przez testowany kod do izolacji
      3. test nie może zakończyć się niepowodzeniem
        • np .: myStubbedService.getValues ​​() po prostu zwraca wartość wejściową
      4. często wdrażają metody abstrakcyjne
    • Makiety
      1. „nadzbiór” odcinków; może twierdzić, że niektóre metody są wywoływane
        • np. sprawdź, czy myMockedService.getValues ​​() jest wywoływana tylko raz
      2. służy do testowania zachowania testowanego kodu
      3. test może się nie powieść
        • np .: sprawdź, czy myMockedService.getValues ​​() zostało wywołane jeden raz; weryfikacja kończy się niepowodzeniem, ponieważ mój testowany kod nie wywołał myMockedService.getValues ​​()
      4. często kpi z interfejsów

11

zobaczmy Test Doubles:

  • Fałszywe : podróbki to obiekty, które mają działające implementacje, ale nie takie same jak produkcyjne. Takich jak : implementacja w obiekcie Data Access Object or Repository.
  • Stub : Stub to obiekt, który przechowuje predefiniowane dane i używa go do odbierania połączeń podczas testów. Na przykład : obiekt, który musi pobrać niektóre dane z bazy danych, aby odpowiedzieć na wywołanie metody.

  • Mocks : Mocks to obiekty, które rejestrują odbierane połączenia. W zapewnieniu testowym możemy zweryfikować na próbkach, że wszystkie oczekiwane działania zostały wykonane. Takie jak : funkcja wywołująca usługę wysyłania wiadomości e-mail. po więcej sprawdź to .


1
najlepsza odpowiedź moim zdaniem
Ero Stefano

9

Fałszywy to ogólny termin, który może być używany do opisania albo niedopałek lub atrapa obiektu (odręcznie lub w inny sposób), ponieważ zarówno wygląd jak prawdziwy przedmiot.

To, czy podróbka jest kikutem, czy próbą, zależy od tego, jak zostanie wykorzystany w bieżącym teście. Jeśli jest używany do sprawdzania interakcji (potwierdzony przeciwko), jest to próbny obiekt. W przeciwnym razie jest to zalążek.

Podróbki zapewniają sprawne działanie testu. Oznacza to, że czytelnik twojego przyszłego testu zrozumie, jakie będzie zachowanie fałszywego obiektu, bez konieczności czytania jego kodu źródłowego (bez konieczności polegania na zasobach zewnętrznych).

Co oznacza płynny przebieg testu?
Na przykład w poniższym kodzie:

 public void Analyze(string filename)
        {
            if(filename.Length<8)
            {
                try
                {
                    errorService.LogError("long file entered named:" + filename);
                }
                catch (Exception e)
                {
                    mailService.SendEMail("admin@hotmail.com", "ErrorOnWebService", "someerror");
                }
            }
        }

Chcesz przetestować metodę mailService.SendEMail () , aby to zrobić, musisz zasymulować wyjątek w swojej metodzie testowej, więc wystarczy utworzyć klasę Fake Stub errorService, aby zasymulować ten wynik, a następnie kod testowy będzie mógł przetestować Metoda mailService.SendEMail (). Jak widzisz, musisz zasymulować wynik pochodzący z innej klasy ErrorService klasy zależności zewnętrznych.


8

Bezpośrednio z gazety Mock Role, a nie Objects , twórcy jMock:

Identyfikatory są fałszywymi implementacjami kodu produkcyjnego, które zwracają wyniki w puszkach. Obiekty próbne działają jak kody pośredniczące, ale zawierają także twierdzenia w celu instrumentowania interakcji obiektu docelowego z jego sąsiadami.

Główne różnice to:

  • Oczekiwania ustawione na kody pośredniczące są zazwyczaj ogólne, podczas gdy oczekiwania dotyczące makiet mogą być bardziej „sprytne” (np. zwracają to przy pierwszym wywołaniu, to przy drugim, itd.).
  • kody pośredniczące są używane głównie do konfigurowania pośrednich danych wejściowych SUT , podczas gdy symulacje mogą służyć do testowania zarówno pośrednich danych wejściowych, jak i pośrednich danych wyjściowych SUT.

Podsumowując, jednocześnie starając się rozproszyć zamieszanie związane z tytułem artykułu Fowlera : kpiny to odcinki, ale nie tylko odcinki .


1
myślę, że masz rację, ale właśnie dlatego artykuł Fowlera jest mylący, tytuł artykułu brzmi „Mocks Aren't Stubs” ... ale tak jest ?! ¯_ (ツ) _ / ¯
stonedauwg

@stonedauwg, w rzeczy samej, edytowałem mój post, aby uwzględnić twoją grę słów i wyjaśnienie. Mam nadzieję, że to pomoże trochę więcej.
Dimos,

@stonedauwg, makieta nie jest odgałęzieniem, podobnie jak prostokąt nie jest kwadratem. :)
seanriordan08,

7

Czytałem The Art of Unit Testing i natknąłem się na następującą definicję:

Fałszywy to ogólny termin, który może być używany do opisania albo niedopałek lub atrapa obiektu (odręcznie lub w inny sposób), ponieważ zarówno wygląd jak prawdziwy przedmiot. To, czy podróbka jest kikutem, czy próbą, zależy od tego, jak zostanie wykorzystany w bieżącym teście. jeśli jest używany do sprawdzenia interakcji (potwierdzony), jest to obiekt próbny . W przeciwnym razie jest to zalążek .


5

Natrafiłem na ten interesujący artykuł autorstwa UncleBob The Little Mocker . Wyjaśnia całą terminologię w bardzo łatwy do zrozumienia sposób, więc jest przydatny dla początkujących. Artykuł Martina Fowlersa jest trudny do przeczytania, szczególnie dla początkujących, takich jak ja.


4

Stub pomaga nam przeprowadzić test. W jaki sposób? Podaje wartości, które pomagają uruchomić test. Te wartości same w sobie nie są rzeczywiste i stworzyliśmy je tylko w celu uruchomienia testu. Na przykład tworzymy HashMap, aby dać nam wartości, które są podobne do wartości w tabeli bazy danych. Zamiast bezpośrednio wchodzić w interakcję z bazą danych, wchodzimy w interakcję z Hashmap.

Mock to fałszywy obiekt, który uruchamia test. gdzie kładziemy nacisk.


„Więc zamiast bezpośrednio wchodzić w interakcję z bazą danych, wchodzimy w interakcję z Hashmap.” ... ponieważ nie mieliśmy jeszcze czasu na kodowanie modułu bazy danych i nie mogliśmy uruchomić kodu testowego bez użycia kodu pośredniczącego. W przeciwnym razie ta sama mapa będzie kpiną! dobrze?
Boris Däppen

4

Zobacz poniżej przykład makiety vs odcinki przy użyciu frameworka C # i Moq. Moq nie ma specjalnego słowa kluczowego dla kodu Stub, ale możesz także użyć obiektu Mock do utworzenia kodów pośredniczących.

namespace UnitTestProject2
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Moq;
    [TestClass]
    public class UnitTest1
    {
        /// <summary>
        /// Test using Mock to Verify that GetNameWithPrefix method calls Repository GetName method "once" when Id is greater than Zero
        /// </summary>
        [TestMethod]
        public void GetNameWithPrefix_IdIsTwelve_GetNameCalledOnce()
        {
            // Arrange 
            var mockEntityRepository = new Mock<IEntityRepository>();
            mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));

            var entity = new EntityClass(mockEntityRepository.Object);
            // Act 
            var name = entity.GetNameWithPrefix(12);
            // Assert
            mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Once);
        }
        /// <summary>
        /// Test using Mock to Verify that GetNameWithPrefix method doesn't call Repository GetName method when Id is Zero
        /// </summary>
        [TestMethod]
        public void GetNameWithPrefix_IdIsZero_GetNameNeverCalled()
        {
            // Arrange 
            var mockEntityRepository = new Mock<IEntityRepository>();
            mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
            var entity = new EntityClass(mockEntityRepository.Object);
            // Act 
            var name = entity.GetNameWithPrefix(0);
            // Assert
            mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Never);
        }
        /// <summary>
        /// Test using Stub to Verify that GetNameWithPrefix method returns Name with a Prefix
        /// </summary>
        [TestMethod]
        public void GetNameWithPrefix_IdIsTwelve_ReturnsNameWithPrefix()
        {
            // Arrange 
            var stubEntityRepository = new Mock<IEntityRepository>();
            stubEntityRepository.Setup(m => m.GetName(It.IsAny<int>()))
                .Returns("Stub");
            const string EXPECTED_NAME_WITH_PREFIX = "Mr. Stub";
            var entity = new EntityClass(stubEntityRepository.Object);
            // Act 
            var name = entity.GetNameWithPrefix(12);
            // Assert
            Assert.AreEqual(EXPECTED_NAME_WITH_PREFIX, name);
        }
    }
    public class EntityClass
    {
        private IEntityRepository _entityRepository;
        public EntityClass(IEntityRepository entityRepository)
        {
            this._entityRepository = entityRepository;
        }
        public string Name { get; set; }
        public string GetNameWithPrefix(int id)
        {
            string name = string.Empty;
            if (id > 0)
            {
                name = this._entityRepository.GetName(id);
            }
            return "Mr. " + name;
        }
    }
    public interface IEntityRepository
    {
        string GetName(int id);
    }
    public class EntityRepository:IEntityRepository
    {
        public string GetName(int id)
        {
            // Code to connect to DB and get name based on Id
            return "NameFromDb";
        }
    }
}

4

Punkt testowy testu pośredniego i próbnego:

  • Stub to fałszywa implementacja wykonywana przez użytkownika w sposób statyczny, tzn. W Stubie piszącym kod implementacyjny. Więc nie może obsłużyć definicji usługi i warunku dynamicznego. Zwykle odbywa się to w frameworku JUnit bez użycia frameworku.

  • Mock jest również implementacją fikcyjną, ale jego implementacja została wykonana dynamicznie przy użyciu frameworków takich jak Mockito. Możemy więc traktować definicję warunków i usług w sposób dynamiczny, tzn. Symulacje można tworzyć dynamicznie z kodu w czasie wykonywania. Tak więc używając makiety możemy dynamicznie implementować Stuby.


3

Plus przydatne odpowiedzi, jeden z najpotężniejszych punktów korzystania z Mocków niż subskrybcji

Jeśli współpracownik [od którego zależy główny kod] nie jest pod naszą kontrolą (np. Z biblioteki innej firmy),
W tym przypadku stub jest trudniejszy do napisania niż próbowania .


2

Użyłem przykładów python w mojej odpowiedzi, aby zilustrować różnice.

Stub - Stubbing to technika opracowywania oprogramowania służąca do wdrażania metod zajęć na wczesnym etapie cyklu rozwojowego. Są one powszechnie stosowane jako symbole zastępcze dla implementacji znanego interfejsu, gdzie interfejs jest sfinalizowany lub znany, ale implementacja nie jest jeszcze znana ani sfinalizowana. Zaczynasz od kodów pośredniczących, co oznacza po prostu, że zapisujesz tylko definicję funkcji i pozostawiasz kod na później. Zaletą jest to, że nie zapomnisz metod i możesz nadal myśleć o swoim projekcie, widząc go w kodzie. Możesz również ustawić swój kod zwrotny na odpowiedź statyczną, dzięki czemu odpowiedź może zostać natychmiast wykorzystana przez inne części kodu. Obiekty wejściowe zapewniają prawidłową odpowiedź, ale są statyczne bez względu na przekazane dane wejściowe, zawsze otrzymasz tę samą odpowiedź:

class Foo(object):
    def bar1(self):
        pass

    def bar2(self):
        #or ...
        raise NotImplementedError

    def bar3(self):
        #or return dummy data
        return "Dummy Data"

Drwić obiekty są wykorzystywane w próbnych testach oni potwierdzić, że niektóre metody są nazywane na tych obiektach. Sztuczne obiekty to symulowane obiekty, które naśladują zachowanie rzeczywistych obiektów w kontrolowany sposób. Zazwyczaj tworzy się próbny obiekt w celu przetestowania zachowania jakiegoś innego obiektu. Próbki pozwalają nam symulować zasoby, które są niedostępne lub zbyt nieporęczne do testów jednostkowych.

mymodule.py:

import os
import os.path

def rm(filename):
    if os.path.isfile(filename):
        os.remove(filename)

test.py:

from mymodule import rm
import mock
import unittest

class RmTestCase(unittest.TestCase):
    @mock.patch('mymodule.os')
    def test_rm(self, mock_os):
        rm("any path")
        # test that rm called os.remove with the right parameters
        mock_os.remove.assert_called_with("any path")

if __name__ == '__main__':
    unittest.main()

Jest to bardzo prosty przykład, który po prostu uruchamia rm i potwierdza parametr, za pomocą którego został wywołany. Możesz użyć makiety z obiektami, nie tylko funkcjami, jak pokazano tutaj, a także możesz zwrócić wartość, dzięki czemu można użyć makiety do zastąpienia kodu pośredniczącego do testowania.

Więcej na unittest.mock , uwaga w 2.x mock nie jest zawarta w unittest, ale jest modułem do pobrania, który można pobrać przez pip (mock install pip).

Przeczytałem także „The Art of Unit Testing” Roy'a Osherove i myślę, że byłoby wspaniale, gdyby podobna książka została napisana przy użyciu Pythona i przykładów Pythona. Jeśli ktoś wie o takiej książce, udostępnij ją. Twoje zdrowie :)


2

Stub to fałszywy obiekt zbudowany do celów testowych. Próbny to kod pośredniczący, który rejestruje, czy oczekiwane połączenia zostały skutecznie wykonane.


2

Kod pośredniczący to pusta funkcja, która służy do unikania nieobsługiwanych wyjątków podczas testów:

function foo(){}

Mock to sztuczna funkcja używana w celu uniknięcia zależności systemu operacyjnego, środowiska lub sprzętu podczas testów:

function foo(bar){ window = this; return window.toString(bar); }

Pod względem twierdzeń i stanu:

  • Egzaminy są potwierdzane przed zmianą zdarzenia lub stanu
  • Identyfikatory nie są potwierdzane, podają stan przed zdarzeniem, aby uniknąć wykonywania kodu z niepowiązanych jednostek
  • Szpiedzy są konfigurowani jak kody pośredniczące, a następnie potwierdzane po zmianie zdarzenia lub stanu
  • Podróbki nie są potwierdzane, działają po zdarzeniu z zakodowanymi zależnościami, aby uniknąć stanu

Bibliografia


2
+1 za dodanie szpiegów do glosariusza. Ponadto myślę, że masz na myśli „Szpiedzy są konfigurowani jak kpiny”, a nie „Szpiedzy są konfigurowani jak
kikuty


2

Makieta jest zarówno przedmiotem technicznym, jak i funkcjonalnym .

Kpina jest techniczna . Jest on rzeczywiście tworzony przez fałszywą bibliotekę (z tego znane są EasyMock, JMockit i ostatnio Mockito) dzięki generowaniu kodu bajtowego .
Implementacja próbna jest generowana w taki sposób, że możemy ją instrumentować , aby zwracała określoną wartość po wywołaniu metody, ale także kilka innych rzeczy, takich jak sprawdzenie, czy wywołano próbną metodę z określonymi parametrami (ścisłe sprawdzenie) lub dowolnymi parametrami ( brak ścisłej kontroli).

Tworzenie makiety:

@Mock Foo fooMock

Nagrywanie zachowania:

when(fooMock.hello()).thenReturn("hello you!");

Weryfikacja wywołania:

verify(fooMock).hello()

To oczywiście nie jest naturalny sposób na tworzenie / nadpisywanie klasy / zachowania Foo. Dlatego odnoszę się do aspektu technicznego.

Ale makieta jest również funkcjonalna, ponieważ jest instancją klasy, którą musimy odizolować od SUT. A z zarejestrowanymi zachowaniami, moglibyśmy użyć go w SUT w taki sam sposób, jak zrobilibyśmy go z kodem pośredniczącym.


Kod pośredniczący to tylko obiekt funkcjonalny : jest to instancja klasy, którą musimy odizolować od SUT i to wszystko. Oznacza to, że zarówno klasa pośrednicząca, jak i wszystkie urządzenia zachowań potrzebne podczas naszych testów jednostkowych muszą być wyraźnie zdefiniowane.
Na przykład kod pośredniczący hello()musiałby podklasować Fooklasę (lub implementować interfejs, który on ma) i przesłonić hello() :

public class HelloStub extends Hello{    
  public String hello { 
      return "hello you!"; 
  }
}

Jeśli inny scenariusz testowy wymaga zwrotu innej wartości, prawdopodobnie będziemy musieli zdefiniować ogólny sposób ustawiania zwrotu:

public class HelloStub extends Hello{    
  public HelloStub(String helloReturn){
       this.helloReturn = helloReturn;
  }
  public String hello { 
      return helloReturn; 
  }
}

Inny scenariusz: gdybym miał metodę skutków ubocznych (bez powrotu) i sprawdziłbym, czy ta metoda została wywołana, prawdopodobnie powinienem dodać wartość logiczną lub licznik w klasie stub, aby policzyć, ile razy metoda została wywołana.


Wniosek

Kod pośredniczący wymaga często dużego obciążenia / kodu do napisania do testu jednostkowego. Jakiej kpiny zapobiega dzięki zapewnieniu funkcji nagrywania / weryfikacji od razu po wyjęciu z pudełka.
Dlatego obecnie podejście stub jest rzadko stosowane w praktyce wraz z pojawieniem się doskonałych fałszywych bibliotek.


O artykule Martina Fowlera: Nie uważam się za programistę „mockist”, kiedy używam makiet i unikam kodów pośredniczących.
Ale używam makiety, gdy jest to naprawdę wymagane (irytujące zależności) i preferuję testy krojenia i testy mini-integracji, gdy testuję klasę z zależnościami, których kpowanie byłoby narzutem.

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.