W jaki sposób fałszywe obiekty są często niewłaściwie wykorzystywane?

15

Niedawno przeczytałem artykuł, w którym napisano, że fałszywe obiekty są często źle rozumiane i niewłaściwie wykorzystywane. Czy są jakieś szydercze anty-wzory, na które mogę uważać?

Armand
źródło
czy artykuł to czytasz? martinfowler.com/articles/mocksArentStubs.html
keppla
nie ... nie pamiętam dokładnego źródła, ale opublikuję tutaj, jeśli to zrobię
Armand,
Zostałem zabrany do zadania przy przepełnieniu stosu, aby wyśmiewać interfejs API mongodb. Wskazano mi post na blogu, w którym twierdzono, że źle jest kpić z każdej klasy, której sam nie napisałeś. Właściwie nie zgadzam się z tym, ale opinia jest już dostępna.
Kevin,

Odpowiedzi:

13

Nienawidzę patrzeć na drwiny z prostych, konkretnych zajęć. Weźmy na przykład następującą prostą klasę, która nie jest zależna od niczego innego:

public class Person
{
    private readonly string _firstName;
    private readonly string _surname;

    public Person(string firstName, string surname)
    {
        if (String.IsNullOrEmpty(firstName))
        {
            throw new ArgumentException("Must have first name");
        }

        if (String.IsNullOrEmpty(surname))
        {
            throw new ArgumentException("Must have a surname");
        }

        _firstName = firstName;
        _surname = surname;
    }

    public string Name 
    {
        get
        {
            return _firstName + " " + _surname;
        }
    }
}

W każdym teście z tą klasą wolałbym raczej utworzyć prawdziwą i używać jej, zamiast wyciągania jakiegoś interfejsu, takiego jak „IPerson”, używanego kpina i oczekiwania. Używając prawdziwego, twój test jest bardziej realistyczny (masz sprawdzanie parametrów i rzeczywistą implementację właściwości „Nazwa”). W przypadku prostej klasy takiej jak ta nie spowalniasz testów, mniej deterministycznie lub mętniejesz w logice (prawdopodobnie nie będziesz musiał wiedzieć, że nazwa została wywołana podczas testowania innej klasy) - co jest typowym powodem kpin / stubbing

Jako rozszerzenie tego, widziałem również, że ludzie piszą testy, w których próbka jest konfigurowana z oczekiwaniami, a następnie próbka jest wywoływana bezpośrednio w teście. Nic dziwnego, że test zawsze się powiedzie ... hmmmm ...

FinnNk
źródło
Na szczęście szydząc z frameworków, z których korzystałem, mogłem wyśmiewać konkretne klasy, więc wyciąganie interfejsów w niewygodnych punktach nie stanowi problemu.
Armand
5
To nie zmienia problemu - tego rodzaju prostej rzeczy na ogół nie należy wyśmiewać, nawet jeśli używasz czegoś, co rozluźnia techniczne ograniczenia tego, co można wyśmiewać (np. Fałszywy framework, taki jak TypeMock lub język dynamiczny).
FinnNk,
Moją ogólną zasadą zawsze było kpić z zachowania, a nie z danych.
ardave
10

Może to zabrzmieć oczywisto, ale: Nie używaj fałszywych obiektów w kodzie produkcyjnym! Widziałem więcej niż jeden przykład, w którym kod produkcyjny zależał od właściwości niektórych próbnych obiektów ( MockHttpServletRequestna przykład z Springframework).

perdian
źródło
14
mam nadzieję, że wypełniłeś swój święty obowiązek i przesłałeś kod DailyWTF?
keppla
1
W mojej poprzedniej pracy wyraźnie zabroniono nam przesyłania czegokolwiek z naszej bazy kodów do DWTF.
quant_dev
9
@quant_dev: Fakt, że mieli taką politykę, sugeruje przerażające rzeczy na temat ich programistów ...
John Fisher,
1
Nie całkiem. Był to startup, który musiał szybko opracować bazę kodu, aby sprzedać produkt, a następnie zaczął konsolidować i refaktoryzować go, aby spłacić dług techniczny, ponieważ produkt dojrzał, a dalszy rozwój był utrudniony przez (brak) wstępnego projektu. Menedżerowie wiedzieli, że stara baza kodowa to gówno, i zainwestowali czas i zasoby w refaktoryzację, ale nie chcieli ryzykować negatywnego rozgłosu.
quant_dev
Sam wybór skrótów nie wystarczy, byś dostał tylko dailywtf ...
poolie
9

Moim zdaniem jest to nadmierna kontrola wywołania metody na próbach. Wydaje mi się, że jest to praktyka wymuszona przez kilka fałszywych frameworków, takich jak EasyMock, w których domyślnym zachowaniem próbnym jest niepowodzenie za każdym razem, gdy istnieje dodatkowe wywołanie metody, które nie było dokładnie określone wcześniej. Tego rodzaju ścisłe, próbne sprawdzanie metod może prowadzić do kruchych projektów, w których najmniejsza zmiana w kodzie może doprowadzić do niepowodzenia całego zestawu testów, mimo że podstawowa funkcjonalność pozostaje taka sama.

Rozwiązaniem tego problemu jest stosowanie kodów pośredniczących zamiast próbnych. Artykuł szczególnie interesujący na ten temat został znaleziony w Javadoc Mockito: http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html (patrz „2. Co powiesz na stubowanie?”). ), link do: http://monkeyisland.pl/2008/07/12/should-i-worry-about-the-u nieoczekiwany/ .

Do tej pory podobała mi się praca z Mockito, ponieważ nie wymusza to ścisłego kpiącego zachowania, ale zamiast tego stosuje kody pośredniczące. Wymusza także sprawdzanie metod na konkretnych zamiast całego próbnego obiektu; więc kończysz sprawdzanie tylko tych metod, które naprawdę mają znaczenie w twoim scenariuszu testowym.

Jest tu kilka książek i mogę polecić dotknięcie tego tematu, kpiny i ogólne:

Wzory xUnit

Sztuka testowania jednostkowego: z przykładami w .Net

Testy Java nowej generacji: TestNG i zaawansowane koncepcje (ta książka jest głównie o testNG, ale jest ładny rozdział o kpinach)

Fabio Kenji
źródło
+1 za punkt nadmiernej kontroli wywołania metody Jednak zawsze jest druga strona medalu, w której nieoczekiwane wywołanie metody powoduje błąd w metodzie. Na szczęście Mockito ma Answer.RETURNS_SMART_NULLSustawienia dla prób, które pomagają to zdiagnozować.
Bringer128,
4

W swoim doświadczeniu zaobserwowałem kilka anty-wzorów.

  • Klasy domen są wyśmiewane / wprowadzane w stan, w którym może wystąpić zmiana stanu i należy to zweryfikować.
  • Testy integracyjne wchodzące w interakcję z mieszanką próbnych i konkretnych klas, co przeczy celowi testów integracyjnych.
  • Nieumyślne użycie prób w kodzie produkcyjnym (nigdy nie powinno się to zdarzyć)

W przeciwnym razie moje doświadczenia z kpiną, zwłaszcza z Mockito, były proste. Sprawili, że testy są bardzo łatwe do napisania i utrzymania. Testowanie interakcji widok / prezenter GWT jest znacznie łatwiejsze w przypadku próbnych testów niż GWTTestCase.

Vinod R.
źródło
2 i 3 to konkretne problemy! Czy masz prosty przykład (1)?
Armand
2

Uważam, że testy, które wykorzystują próby na wielu warstwach aplikacji, są szczególnie trudne do odczytania i zmiany. Myślę jednak, że zostało to złagodzone w ostatnich latach dzięki ulepszonemu interfejsowi API typu mock framework (używam JMock tam, gdzie jest to wygodne).

5 lub 6 lat temu API takie jak EasyMock były potężne, ale bardzo uciążliwe. Często kod testowy, który go wykorzystywał, był o rząd wielkości bardziej skomplikowany niż kod, który testował. Próbowałem wtedy wpływać na zespoły, z których korzystałem bardzo oszczędnie i radziłem sobie z prostymi ręcznie wykonanymi próbami, które były po prostu alternatywnymi implementacjami interfejsów specjalnie do testowania.

Ostatnio moje mocne opinie na ten temat stały się łagodniejsze, ponieważ szydercze interfejsy API sprawiły, że testy, które wykorzystują je, są bardziej czytelne. Zasadniczo chcę, aby mój kod (w tym testy) mógł być zmieniany przez innych programistów bez powodowania, że ​​czują się, jakby przesiewali przez bagno niejasnych wywołań API.

rupjones
źródło