Czemu używacie mockito/innych libek do mocków, zamiast fake objectów?

1
Aventus napisał(a):

Dla tego napisałem że w większości przypadków. Z tym że moim zdaniem to o czym piszesz to znaczna mniejszość, sytuacje takie jak wspomniał jarek czyli np. wysłanie maila. Ale wiadomo, każdy ma inne doświadczenia.

Być może mniejszość, ja po prostu wspomniałem o tym aspekcie, który wszyscy tutaj zdają się ignorować pisząc, że "mockowanie jest bez sensu".

2

Biblioteki do mockowania ułatwiają (w części przypadków) tworzenie fake objects. W przypadku z OP nie specjalnie widzę różnicę pomiędzy użyciem mocka, napisaniem fejkowej implementacji interface'u za po mocą klasy anonimowej, testowej, lambdy itd. Tak długo jak jest to czytelne oczywiście.

Druga sprawa - jaki jest sens pisania testu do interface'u?

2
piotrpo napisał(a):

Druga sprawa - jaki jest sens pisania testu do interface'u?

No oczywiście że to nie jest interfejsu :| Tylko test klasy, której interfejs jest parameterem. Mamy opcję zamockować interfejs, albo dodać fake'a.

2

A czemu miałbym pisać coś z palca co zwykle jest łatwiej ustawić?

Co do tego czy testować z użyciem mocka czy testować całość jako jednostkę i mocka nie używać a rzeczywistej implementacji - to osobny temat.
Tak samo czy jest sens testować service który wywołuje 3 inne i nic nie zwraca, a także nie ma w sobie żadnej logiki jednostkowo. Jeśli miałbym tam użyć mocków to mogę sobie ten test... no. Nic nie daje. Lepiej to faktycznie przetestować jako część testu integracyjnego (w sensie nie muszę zaczynać w tej klasie, może być po prostu używana w scenariuszu testu integracyjnego albo e2e).

Kolejną zaletą użycia mocka jest to, że nie potrzebuję robić 3 różnych faków do 3 różnych scenariuszy, ani skomplikowanej logiki - jeśli wywołam z A to daj mi 1, jak z B to daj mi 2.... a jak BIGJGJJG to 546456 ale jak z BIGGJJG to rzuć błędem. Praca z czymś takim to koszmar. Wolę mieć scenariusz, w nim gdzieś zapisane jak wywołuję X to dostaję 5 i nic mnie więcej nie obchodzi. Prosto i czytelnie i bez dużej liczby WTF/minutę.

Podkreślę jeszcze raz: trzeba się najpierw zastanowić czy mocka lub fake w ogóle używać,a jeśli tak to moim zdaniem już użyć biblioteki.

3

Odnoszę wrażenie, że zła reputacja bibliotek do mockowania wynika częściowo z faktu, że ktoś powiedział, że mockowanie jest złe. Z tego samego powodu hejtuje się masę innych technologii, jak JPA, Hibernate, Springa, ORM, kontenery IoC itd. W takiej sytuacji nawet jak się poda konkretny use case to jak krew w piach, bo przelatuje to koło uszu.

superdurszlak napisał(a):

Dwa, że tak przetestowany kod jest bardzo odporny na jakiekolwiek refaktoringi - możesz przepisać kod na taki, który działa identycznie, ale wszystkie testy zfailują bo nie zgadzają się wywołania.

Kod jest tak odporny na refactoring jak sobie to sami zgotujemy, był tu już przykład z Mailerem, ale no przecież nikt nie wywołuje zewnętrznych zależności w kodzie to po co to komu xD Czy interfejs EmailSender i sprawdzenie wywołania w teście to jest to co blokuje jakikolwiek refactoring?

3

Po pierwsze i najważniejsze - spytam tak: po co miałbym pisać za każdym razem sztuczną implementację obiektu zamiast korzystać z Mockito?
Tak na serio - nie widzę żadnego powodu - poza puryzmem językowym, którego specjalnie nie trawię - żeby specjalnie pisać fake objects.

Druga sprawa: dla interfejsów funkcyjnych sprawa jest prosta. Ale co jeśli interfejs ma pięć metod?

Np.

interface Taxi {
    int doSomethin1(String s);
    int doSomethin2(String s);
    int doSomethin3(String s);
    int doSomethin4(String s);
    int doSomethin5(String s);
}

Miałbym sobie stworzyć FakeTaxi, który w konstruktorze będzie przyjmował pięć elementów, z których wykorzystam jeden-dwa na test? Dzięki Mockito widząc test widzę od razu, co powinno być zwrócone.

A teraz hardmode - co jeśli chciałbym, żeby w jednym teście:

  • metoda doSomethin1 w zależności od podanego Stringa zwracała różną wartość
  • metoda doSomethin2 zawsze zwracała tę samą wartość
    A w drugim na odwrót? Przekazywać lambdy, przez co kod będzie wyglądał tak:
Taxi taxi = new FakeTaxi(
    e -> {
        if (e.equals("A") { return 1; }
        else if (e.equals("B") { return 2; }
        else if (e.equals("C") { return 3; }
        else { throw new RuntimeException(); }
    }, 
    e -> 1,
    e -> { throw new RuntimeException(); },
    e -> { throw new RuntimeException(); },
    e -> { throw new RuntimeException(); }
);

Taxi taxi2 = new FakeTaxi(
    e -> 1,
    e -> {
        if (e.equals("A") { return 1; }
        else if (e.equals("B") { return 2; }
        else if (e.equals("C") { return 3; }
        else { throw new RuntimeException(); }
    }, 
    e -> { throw new RuntimeException(); },
    e -> { throw new RuntimeException(); },
    e -> { throw new RuntimeException(); }
);

I to wszystko dlatego, że ktoś kiedyś na jakiejś konferencji usłyszał, że Mockito jest złe? xD

1

Po co testowac metody ktore nie daja efektu?
Ja zwykle testuje wynik dla podanych parametrow kompletnie ignorujac co sie i ile razy wywoluje.

1
somekind napisał(a):

Być może mniejszość, ja po prostu wspomniałem o tym aspekcie, który wszyscy tutaj zdają się ignorować pisząc, że "mockowanie jest bez sensu".

Ja tego aspektu nie zignorowałem, a wręcz się do niego odniosłem. Wspomniałeś o tym cytując moją wypowiedź więc założyłem że odnosisz się bezpośrednio do mnie, a nie ogólnie.

Tak czy inaczej mam nadzieję że jest jasne, że ja zgadzam się co do tego że mocowanie jest nadużywane, natomiast nie twierdzę aby było ogólnie bez sensu.

0

musicie pracowac z malym dobrym systemem, jezeli korzystanie z prawdziwych obiektow jest tak proste jak w pierwszym poscie. Co jak klasa ma N zaleznosci, a kazda z zaleznosci ma swoje N zaleznosc itd itd

1
Saalin napisał(a):

Kod jest tak odporny na refactoring jak sobie to sami zgotujemy, był tu już przykład z Mailerem, ale no przecież nikt nie wywołuje zewnętrznych zależności w kodzie to po co to komu xD Czy interfejs EmailSender i sprawdzenie wywołania w teście to jest to co blokuje jakikolwiek refactoring?

Jeden? Nie. Kilkadziesiąt? Sam sobie odpowiedz.

Dla mnie takie cudaki jak verify mogą być użyteczne w sytuacji, gdy dostajesz w prezencie kupę gruzu do utrzymania, testów nie ma i nie za bardzo da się napisać lepsze bez przepisywania wszystkiego. Skoro strach refaktorować, a bez refaktora nie napiszesz testów opartych wyłącznie o zwracane rezultaty / sprawdzanie efektów, no to trochę wyjścia nie ma. Natomiast pisanie nowego kodu i testowanie go w oparciu o verify a już szczególnie inorder albo argumentcaptor to strzelanie sobie w kolano.

Co do sprawdzania efektów - te też można sprawdzać nieco inaczej, niż patrząc ile razy Twój interfejs EmailSender został zawołany. Jeśli nic nie zwraca i nie może niczego sensownie zwracać, to możesz mieć implementację EmailSender która pisze sobie do pamięci, zamiast coś naprawdę wysyłać (w końcu to unit test i nie testujemy zewnętrznej zależności). I potem można ładnie podejrzeć, jakie efekty wykonał EmailSender bez sprawdzania, jak został zawołany i z jakimi argumentami.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0