Jak to jest z tymi mockami?

1

Ostatnio rozpocząłem prace w zupełnie innej dziedzinę zarówno pod względem technologii jak i produktu. Piszę głównie w Go, trochę mikroserwisow, z którymi jeszcze nie miałem do czynienia. W testach masa rzeczy jest zamockowana. Z jednej strony wygląda to "ładnie", a drugiej okazuje się że są bugi których te testy najwidoczniej nie łapią.

Pracując w C++ trafiałem na takie projekty gdzie tych mockow praktycznie nie było, a jak już to stanowiły powiedzmy z 10% zawartości testów w bardzo specyficznych przypadkach.

Przechodząc do rzeczy. Kiedy warto stosować mocki? Znacie również jakieś przykłady kiedy te "atrapy" są nadużywane? Co wtedy zrobić jeśli chcę być dobrym samarytaninem i polepszyć kod w którym na codzień się obracam?

4

Kiedy warto stosować? Kiedy nie chcesz testować jakiejś części kodu (bo jest przetestowana osobno) lub na przykłąd łączyć do rzeczywistej bazy danych i robisz implementację która szybciej będzie udawać wynik tamtej części kodu.
Czy są nadużywane? Często i gęsto. Praktycznie we wszystkich projektach w jakich pracowałem były nadużywane - potem mamy testy wydmuszki które tworzą mocki i je wywołują nie testując praktycznie niczego poza frameworkiem do mockowania.
Powinno się używać jak najmniej, test powinien przejść przez jak najwięcej rzeczywistego kodu.
Co zrobić? Usuwać mocki, testować rzeczywisty kod. Sam będziesz wiedział kiedy trzeba użyć mocka.

6

Mocki można stosowac w przypadku zależności zewnętrznych IO kiedy nie robimy testów integracyjnych. Można od bidy stosowac nawet do wewnętrznych zależności, ale to w jakis skrajnych przypadkach.
Ale osobiście preferuje TestContainers i wtedy człowiek nie narzeka ;]

0

@obscurity: To czegoś tu nie rozumiem.
Przykładowo mamy A zależne od B (nie wnikając skąd ta zależność i czy faktycznie powinna istnieć). W takim razie robimy faktyczne testy B, a w testach A tworzymy sobie zmockowane B. Wydaje się proste jak budowa cepa. Skąd w takim razie bierze się ta patologia związana z mockami? Właśnie ze względu na jakieś niepoprawne zależności?

4
Seken napisał(a):

@obscurity: To czegoś tu nie rozumiem.
Przykładowo mamy A zależne od B (nie wnikając skąd ta zależność i czy faktycznie powinna istnieć). W takim razie robimy faktyczne testy B, a w testach A tworzymy sobie zmockowane B. Wydaje się proste jak budowa cepa.

A czy w tej historii B jest oddzielną jednostką, czy częścią jednostki A? Bo jeśli to drugie, to właśnie opisałeś nadużycie mocka.

Skąd w takim razie bierze się ta patologia związana z mockami? Właśnie ze względu na jakieś niepoprawne zależności?

Z dwóch powodów:

  1. Betonowanie kodu testującego z produkcyjnym. Potem nie można zrobić normalnej refaktoryzacji jednostki A (np. zamiast B użyć C oraz D), bo testy przestaną się kompilować. Musisz naprawić testy, zrobić mocki C i D zamiast B, a więc kończysz de facto z nowymi testami A oraz nową implementacją A. Mogłeś zrobić błędy w implementacji oraz odpowiadające błędy w testach, przez co kończysz z błędnym kodem oraz niewykrytymi błędami. Ergo poprzednie testy były bezużyteczne, bo nie dały Ci bezpieczeństwa refaktoryzacji.
  2. Stosując mocki zamiast faktycznych implementacji łatwo stworzyć testy, które tak naprawdę nie testują rzeczywistych sytuacji. Ponieważ mock robi to, co mu każemy, zamiast wywalić się, gdy np. klasa testowana przekaże mu bezsensowne dane, to efekt będzie taki, że mimo zielonych testów będziemy mieli błędny kod.
2

Generalnie, mocków używamy tam gdzie nie chcemy czegoś testować. Będą to przede wszystkim operacje na plikach, bazach danych, komunikacja sieciowa. Czasem również możemy chcieć zmockować inną część naszego kodu, żeby ograniczyć obszar testu. Dobrym pomysłem jest sprawdzanie test-coverage, czyli czy wszystkie ścieżki wykonywania są pokryte. Oprócz tego, wiadomo, testy integracyjne, itp. Poza tym, pisząc testy trzeba oczywiście pamiętać o warunkach brzegowych; w końcu test-coverage wykryje tylko czy przetestowane jest to co w kodzie, a nie domyśli się jakie przypadki nie są uwzględnione. :)

2

Mocki mogą znaczyć dwie rzeczy:

  • Dla niektórych to jest po prostu dostarczenie do klasy testowej implementacji (fake class, stub, anonimowa implementacja klasy) - ja takich używam często, zawsze kiedy potrzebuję przekazać swoją implementację w testach (nie zależnie od tego czy to coś pod spodem to jest coś z i/o, czasem, siecią). Po prostu, jeśli uważam że rozdzielenie dwóch klas jest wartościowe, to wsadzam swoje implementacje.
  • Dla innych to jest pisanie tworzenia dynamicznych mocków z użyciem jakiejś biblioteki, taki kod potem wygląda tak mock.getUsers().thenReturn(fakeUsers()). I takie tworzenie mocków z użyciem takiej biblioteki zaciemnia testy jak się tylko da, bo 80% kodu testów to jest rypanie się z mockami, i może 20% jest prawdziwego testowania. Nie mówiąc o innych problemach, jak to żę czasem powstają testy które testują tylko mocki, a nie prawdziwy kod.
0
Riddle napisał(a):

Mocki mogą znaczyć dwie rzeczy:

  • Dla niektórych to jest po prostu dostarczenie do klasy testowej implementacji (fake class, stub, anonimowa implementacja klasy) - ja takich używam często, zawsze kiedy potrzebuję przekazać swoją implementację w testach (nie zależnie od tego czy to coś pod spodem to jest coś z i/o, czasem, siecią). Po prostu, jeśli uważam że rozdzielenie dwóch klas jest wartościowe, to wsadzam swoje implementacje.

Klasy testowej (czyli tej w ktorej przebiegaja testy) czy testowanej? Jezeli mamy jakas metode bool foo() to po prostu tworzysz jej osobna implementacje na potrzeby testow, ktora zwraca na sztywno true/false? Dobrze rozumiem czy chodzi o cos innego?

1
Seken napisał(a):
Riddle napisał(a):

Mocki mogą znaczyć dwie rzeczy:

  • Dla niektórych to jest po prostu dostarczenie do klasy testowej implementacji (fake class, stub, anonimowa implementacja klasy) - ja takich używam często, zawsze kiedy potrzebuję przekazać swoją implementację w testach (nie zależnie od tego czy to coś pod spodem to jest coś z i/o, czasem, siecią). Po prostu, jeśli uważam że rozdzielenie dwóch klas jest wartościowe, to wsadzam swoje implementacje.

Klasy testowej (czyli tej w ktorej przebiegaja testy) czy testowanej? Jezeli mamy jakas metode bool foo() to po prostu tworzysz jej osobna implementacje na potrzeby testow, ktora zwraca na sztywno true/false? Dobrze rozumiem czy chodzi o cos innego?

Nie uważam żeby wkładanie mocków tam gdzie się da, było zasadne. Jeśli możesz napisać test który testuje cały moduł, i nie wymaga odizolowania go od niczego to fake'i i mocki nie mają sensu.

Ja dodaje fake'owe klasy tylko w tych miejscach, w których nie chcę uzależnić test suite'a od zależności. Wtedy tak, tworzę sobie powiedzmy new MemoryClient() i on jest inną implementacją Client, którą dostaje moja testowana klasa. Nie robię cyrków w stylu Client client = Mockito.mock(Client.class);.

8

Mocków nie należy stosować nigdy. W ostateczności prawie nigdy.
To prawie nigdy to, bo są oczywiste wyjątki:

  • testujemy odpalenie bomby atomwej,
  • testujemy coś z bazą danych oracle, a klauzula sumienia, nie pozwala na odpalenie tej kupy na własnym kompie (nawet w kontenerze) - wtedy mockujemy przez H2, (inne bazy podnosimy np. przez testcontainers)

I jeszcze jedno - nie polecam pisania testów jednostkowych.
Piszcie po prostu testy.

0
jarekr000000 napisał(a):
  • testujemy coś z bazą danych oracle, a klauzula sumienia, nie pozwala na odpalenie tej kupy na własnym kompie (nawet w kontenerze) - wtedy mockujemy przez H2, (inne bazy podnosimy np. przez testcontainers)

a Oracle wspiera returning dla całych wierszy? Bo H2 nie wspiera.
Kod:

UPDATE products SET price = price * 1.10
  WHERE price <= 99.99
  RETURNING name, price AS new_price;

nie zadziałą w H2, chociaż kod:

INSERT INTO users (firstname, lastname) VALUES ('Joe', 'Cool') RETURNING id;

zadziała, bo H2 wspiera returning dla kluczy głównych

1

Według moich doświadczeń mocki są nadużywane jedynie w wypadku kiedy mamy jakiegoś stęchłego Behemota, na którym już 2 pokolenia programistów odłożyły kasę na emeryturę, a ktoś wpadł na idiotyczny pomysł "jak pokryjemy to testami, to podniesie się jakość". Większość narzędzi niby-wspomagających "testowanie" ma zastosowanie jedynie przy pokrywaniu g... pazłotkiem, bo ktoś tak kazał. Jeżeli mamy jakies fabryki, wstrzykiwanie na polach, porąbane w 3d inicjalizacje obiektów, to faktycznie nie da się pokryć testami bez mocków, a przetestować nie da wcale.
W przypadku kiedy idzie się z kodem od strony testów, to dokładnie wiadomo, kiedy trzeba będzie użyć jakiegoś mocka i najczęściej będzie to zewnętrzny serwis. Przy okazji uniknie się pisania i przepisywania masy kodu, który na koniec okazuje się zbędny, ale nie usuwa się go, bo szkoda i zostają jedynie jakieś głupoty wynikające z języka, typu obowiązkowo przechwycony wyjątek, który nie ma prawa się zdarzyć w rzeczywistości.

2
piotrpo napisał(a):

W przypadku kiedy idzie się z kodem od strony testów, to dokładnie wiadomo, kiedy trzeba będzie użyć jakiegoś mocka i najczęściej będzie to zewnętrzny serwis. Przy okazji uniknie się pisania i przepisywania masy kodu, który na koniec okazuje się zbędny, ale nie usuwa się go, bo szkoda i zostają jedynie jakieś głupoty wynikające z języka, typu obowiązkowo przechwycony wyjątek, który nie ma prawa się zdarzyć w rzeczywistości.

Nie, nie wiadomo. Wielu ludzi uczy się testowania ze słabych tutoriali, w których powielana jest bzdura o tym, że jednostką jest klasa, i każdą klasę trzeba testować oddzielnie z zamockowanym wszystkimi zależnościami. Wewnętrzne, zewnętrzne, stable, volatile, nie ma znaczenia - wszystko, to wszystko.
I oczywiście każda klasa musi mieć interfejs. Im więcej interfejsów tym bardziej profesjonalnie.

0

@somekind: Jeżeli piszesz od strony testów (TDD), to jak masz dojść do sytuacji, w której jakiegoś obiektu nie da się stworzyć, albo napisać przypadkowy test jakiejś tam klasy w głębokich bebechach? I po co stworzysz interface, który nie jest ci do niczego potrzebny? Jedyna sytuacja, to kiedy w trakcie "zazieleniania" testu integracyjnego dojdziesz do jakiegoś konkretnego miejsca i dopiszesz test konkretnej metody, żeby sobie ułatwić jej naprawienie.
Swoją drogą, to właśnie to podejście top->down jest moim zdaniem największą korzyścią z całego tego TDD, wcale nie testy.

1

Nie wiem jak, nie wiem po co.
Ludzie tak robią. Po prostu.

1
piotrpo napisał(a):

@somekind: Jeżeli piszesz od strony testów (TDD), to jak masz dojść do sytuacji, w której jakiegoś obiektu nie da się stworzyć, albo napisać przypadkowy test jakiejś tam klasy w głębokich bebechach? I po co stworzysz interface, który nie jest ci do niczego potrzebny? Jedyna sytuacja, to kiedy w trakcie "zazieleniania" testu integracyjnego dojdziesz do jakiegoś konkretnego miejsca i dopiszesz test konkretnej metody, żeby sobie ułatwić jej naprawienie.
Swoją drogą, to właśnie to podejście top->down jest moim zdaniem największą korzyścią z całego tego TDD, wcale nie testy.

Masz dwie opcje. Albo kod jest testowalny, albo nie jest.

  • Jeśli jest testowalny, to po prostu instancjonujesz daną klasę w testach i przekazujesz normalnie jej zależności (prawdziwe lub fałszywe, do testów).
  • Jeśli kod jest nietestowalny (czyli np tak jak mówisz, ktoś na samym dole sobie instancjonuje coś czego nie da się podmienić) to i tak jesteś w czarnej pupie, i te mocki to jest Twój najmniejszy problem.

Jedyne co możesz wtedy zrobić to zrefaktorować kod, tak żeby był testowalny.

1

Sytuacja z dzisiaj odnosnie mojego tematu. Mamy pewien mikroserwis do przechowywania danych i napisanego do niego klienta. Na testach CI wszystko zielone, pokrycie bliskie 100%.
Mechanika dzialania jest prosta, server ma swoj wlasny token, ktory klient musi podac podczas komunikacji. No i okazuje sie, ze mimo ze instancja klienta jest konstruowana wraz z tym tokenem to pewna metoda ktora wrzuca token do contextu nigdzie, zupelnie nigdzie nie jest wywolywana :D. Niech zyja mocki.

1

A jak testujecie endpoint GET /products, to

  1. Najpierw (w teście) robicie POST /products, a potem sprawdzacie, czy to się spina z tym, co zwraca GET /products. Tutaj trzeba założyć, że POST /products jest przetestowany i działa poprawnie.
  2. "Ręcznie" wrzucacie dane do bazy i potem sprawdzacie, czy to się spina z tym, co zwraca GET /products
  3. Mockujecie bazę i to, co ma zwracać SELECT * FROM products
  4. Używacie jakiegoś helpera do stworzenia produktów (wtedy nie interesuje nas, czy ten helper robi pod spodem POSTa na /products czy INSTERTy do bazy)
  5. Macie gdzieś przygotowaną bazę z kilkoma produktami, w teście startujecie aplikację z tą bazą i sprawdzacie, czy GET /products zwraca to, co ma zwracać? to w sumie podobne do p. 4, tylko mniej explicit

A może testowanie samego /GET products jest złym podejściem, i jeden test powinien sprawdzić zarówno POST /products, jak i GET /products?

0
  1. Brzmi jak jedna z opcji
  2. Nie, bo to spina za bardzo interfejs i persystencję
  3. J.w.
  4. Brzmi jak opcja
  5. Raczej nie, to uzależniłoby testy od siebie

Ja w swoich projektach wybieram opcję C., czyli testuje tylko i wyłącznie interfejs http, bez tego co jest pod spodem, tzn sprawdzam tylko czy taki o taki request http został zmapowany na taki i taki request domenowy.

Ludzie często myślą że endpointy, logika i recordy w bazie to "jedna i ta sama aplikacja", ale aplikacją jest tylko logika, a http oraz persystencja to są dodatkowe zależności które należy po pierwsze przetestować osobno, a po drugie nie wiązać ich razem.

1

@iksde: Bramka #1 (podejście koszerne) jest pierwszym testem jaki bym napisał, albo drugim, po tym jak pierwszy byłby post>delete>get, bo jakoś trzeba po tych testach sprzątać, żeby je od siebie uniezależnić.
Helpery, gotowe bazy itd. to coś, co czasami trzeba zastosować, ale IMO, lepiej iść "normalnie", tak długo jak się da.

@Riddle:

Ludzie często myślą że endpointy, logika i recordy w bazie to "jedna i ta sama aplikacja"

Ale jeżeli twoja historyjka brzmi zrób CRUD'a do produktów pod adresem ../products to fajnie mieć test, który dokładnie to przetestuje.

1
Riddle napisał(a):

Ludzie często myślą że endpointy, logika i recordy w bazie to "jedna i ta sama aplikacja", ale aplikacją jest tylko logika, a http oraz persystencja to są dodatkowe zależności które należy po pierwsze przetestować osobno, a po drugie nie wiązać ich razem.

Czyli zakładając, że masz jakiś ProductController, ProductRepository i ProductService przetestowałbyś:

  1. ProductController, mockując ProductService
  2. ProductService, mockując ProductRepository
  3. ProductRepository na "żywej" bazie?
piotrpo napisał(a):

@iksde: Bramka #1 (podejście koszerne) jest pierwszym testem jaki bym napisał, albo drugim, po tym jak pierwszy byłby post>delete>get, bo jakoś trzeba po tych testach sprzątać, żeby je od siebie uniezależnić.

Riddle napisał(a):
  1. Raczej nie, to uzależniłoby testy od siebie

Zakładam, że każdy test stawia sobie swoją instancję apki (i wszystkich zależności, w tym przypadku bazy), więc nie trzeba nic sprzątać ani matrwić się o to, że testy mogą sobie wchodzić w drogę.

1
piotrpo napisał(a):

Ale jeżeli twoja historyjka brzmi zrób CRUD'a do produktów pod adresem ../products to fajnie mieć test, który dokładnie to przetestuje.

I tym samym przywiązać interfejs, logikę i persystencje, sprawiając że zmiany w tych rejonach potem są trudniejsze? Tak, super plan.

Wydaje Ci się że przez tight-coupling osiągniesz lepsze zabezpieczenie przed bugami, ale w rzeczywistości tylko betonujesz implementację.

0

Najważniejsze, żeby testy były inteligentne i testował coś konkretnego jakiś kornre case.

Z doświadczeniem zaczniesz zauważać szczegóły, które mogły by umknąć i wtedy dobrze to otestować, żeby inni też o tym wiedzieli.

1
iksde napisał(a):

A jak testujecie endpoint GET /products, to

  1. Najpierw (w teście) robicie POST /products, a potem sprawdzacie, czy to się spina z tym, co zwraca GET /products. Tutaj trzeba założyć, że POST /products jest przetestowany i działa poprawnie.

Tak jest dobrze.

A może testowanie samego /GET products jest złym podejściem, i jeden test powinien sprawdzić zarówno POST /products, jak i GET /products?

Tak też jest dobrze.

iksde napisał(a):

Zakładam, że każdy test stawia sobie swoją instancję apki (i wszystkich zależności, w tym przypadku bazy), więc nie trzeba nic sprzątać ani matrwić się o to, że testy mogą sobie wchodzić w drogę.

Na serwerze CI też nie trzeba sprzątać?

Riddle napisał(a):

Wydaje Ci się że przez tight-coupling osiągniesz lepsze zabezpieczenie przed bugami, ale w rzeczywistości tylko betonujesz implementację.

To, że zdeployowane API odbiera request HTTP, wywołuje kod i zapisuje coś do bazy, to nie jest żaden tight coupling.

1
somekind napisał(a):
Riddle napisał(a):

Wydaje Ci się że przez tight-coupling osiągniesz lepsze zabezpieczenie przed bugami, ale w rzeczywistości tylko betonujesz implementację.

To, że zdeployowane API odbiera request HTTP, wywołuje kod i zapisuje coś do bazy, to nie jest żaden tight coupling.

Może nie koniecznie to się nazywa tight-coupling.

Ale to że coś siedzi w bazie to szczegół implementacyjny Twojej persystencji, o którym testy nie powinny wiedzieć

Albo innymi słowy, nie powinny zacząć failować jeśli ją zrefaktorujesz.

Testy są tak dobre jak ich odporność na zmiany - jeśli robisz rename, albo coś związanego z bazą, i Twoje testy failują to płacisz ich niepotrzebny koszt

Mało ludzi na tym forum rozumie ideę dobrych testów, mało ludzi doświadczyło jak niskim kosztem i wysiłkiem można wprowadzać duże poprawki do dobrze przetestowanego kodu. Wielu ludzi woli dopasować testy do swojej wizji struktury aplikacji, i potem pracować z nieelastycznym kodem.

0
iksde napisał(a):

Czyli zakładając, że masz jakiś ProductController, ProductRepository i ProductService przetestowałbyś:

  1. ProductController, mockując ProductService
  2. ProductService, mockując ProductRepository
  3. ProductRepository na "żywej" bazie?

Jak widzę takie nazwy, to już wiem że ten kod jest napisany w "pseudo obiektowy sposób" i i tak cała logika tej aplikacji jest rozsiana po tych klasach i nie da się jej przetestować w odizolowaniu.

Należałoby je zrefaktorować w taki sposób, żeby faktycznie wszystko czego wymagałbym od systemu jest w tej klasie "service", ale nie sądzę że to można powiedzieć o takim przykładzie. Myślę że service i tak za dużo wie o persystencji, a controller za dużo o service. Myślę też że gdybym zaproponował jakie zmiany należałoby wprowadzić, żeby należycie przetestować te klasy to odpowiedziałbyś "nie ma to sensu " albo "nie ma na to czasu ", tym samym tracąc szanse na dobre przetestowanie tego.


Punkt 1. Na pewno nie. Na pewno nie do przetestowania logiki
Punkt 2. Mocować lub nie mockowac, to zależy
Punkt 3. Ale Zależy czy miałbym je napisać żeby przetestować nimi tylko persystencje czy ten kawałek persystencji o którym logika wie. Tak, na żywej bazie, najpewniej in memory albo na SQLite jeśli klient wspiera różne dialekty. Jeśli nie to in-memory jakąś, a jeśli się nie da to na prawdziwej. Cokolwiek co zapewni szybkie testy, a jednocześnie nie uposledzi testów do poziomu bezużyteczności.

0
iksde napisał(a):

Zakładam, że każdy test stawia sobie swoją instancję apki (i wszystkich zależności, w tym przypadku bazy), więc nie trzeba nic sprzątać ani matrwić się o to, że testy mogą sobie wchodzić w drogę.

To brzmi jak 100-200 testów w minute. Wolne testy to zło. Na moje standardy dużo za wolno. Chyba że masz jakiś sposób jak postawiać całą aplikacje i odpalić dużo testów szybko. Ja bym chciał tak 2000-3000 testów w 5-10 sekund.

1
Riddle napisał(a):

Ale to że coś siedzi w bazie to szczegół implementacyjny Twojej persystencji, o którym testy nie powinny wiedzieć

Zgoda, ale przecież testy, które wywołują endpoint nie wiedzą nic o bazie.
Mam wrażenie, że @piotrpo miał na myśli testy integracyjne gdy pisał o testowaniu CRUDa.

Riddle napisał(a):

Jak widzę takie nazwy, to już wiem że ten kod jest napisany w "pseudo obiektowy sposób" i i tak cała logika tej aplikacji jest rozsiana po tych klasach i nie da się jej przetestować w odizolowaniu.

Należałoby je zrefaktorować w taki sposób, żeby faktycznie wszystko czego wymagałbym od systemu jest w tej klasie "service", ale nie sądzę że to można powiedzieć o takim przykładzie. Myślę że service i tak za dużo wie o persystencji, a controller za dużo o service.

I wiesz to nie widząc kodu, na podstawie samych nazw klas?

0

Mocki się do niczego nie nadają, albo testujesz test, infrastrukturę, na co testować bibliotekę do testowania.

1

@somekind: Nie wiem czy to co mam na myśli, to testy integracyjne, jednostkowe, e2e czy może da się im nakleić jeszcze jakąś inną etykietkę. Wszystko co próbowałem nieudolnie napisać, to że testy powinny być tworzone maksymalnie wysoko i bez wnikania w sposób implementacji. Jeżeli mam np. mikroserwis, zajmujący się obsługą CRUD jakiegoś obiektu biznesowego, to da się jego funkcjonalność przetestować "black box", czyli z poziomu API, bo wywołanie POST, PUT, DELETE zmienia wynik zwracany przez GET. W takim razie, z tego poziomu powinno dać się przetestować całą funkcjonalność aplikacji.
Problemem jest to, że czasami nie da się tego zaimplementować, bo np. po drodze podnosi się jakiś wielgachny serwer aplikacyjny i trwa to najzwyczajniej w świecie za długo, albo rolą tej usługi jest nie tylko zmiana danych na warstwie persistent, ale również wysyłanie informacji o dokonanych zmianach do jakiejś kolejki. To wrzucenie do kolejki będzie pokryte z automatu, ale nie będzie przetestowanie, bo nie ma tam asercji. Pewnie nie chcemy tego robić na żywym systemie, albo nawet na jakimś środowisku, bo jeżeli 2 programistów w tym samym czasie puści testy, to ich wynik będzie przypadkowy. Więc zasadne stanie się użycie jakiegoś mocka. Tym mockiem może być albo lokalnie odpalona kolejka, albo jakiś mock drivera kolejki, na którym sprawdzimy, czy faktycznie zostało wywołane to co miało zostać wywołane. Wstawienie tego mock'a jest kompromisem pomiędzy tym co chcemy zrobić i jakimiś dodatkowymi ograniczeniami, typu działa za wolno, albo nie chcemy wysyłać tego przelewu naprawdę.

Jeżeli testujemy prostego CRUD'a, to testy ma poziomie REST API pozwalają przetestować całą funkcjonalność, bez nadmiernych kosztów, a jednocześnie to co jest za tym API, nie jest w żaden sposób zabetonowane testami. Możesz sobie nawet podmienić tę Javę na Pythona i testy nadal będą przechodzić, pomijając ewentualne trudności z ich odpaleniem. Jeżeli napiszę testy do kontrolera, to z jednej strony jakiś kawałek logiki aplikacji nie zostanie pokryty testami (mapowanie na endpointy, konfiguracja parsera JSON itp.), z drugiej strony zabetonuję np. nazwy metod tego kontrolera, czyli patrząc tylko na testy - jest gorzej. Nie twierdzę, że nie wolno tak robić, albo, że tak jest źle, ale płacimy jakością testów i muszę sobie zadać pytanie co za to kupuję i czy ta cena nie jest zbyt wysoka.

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