Testy integracyjne - MockMVC vs WebTestClient

0

Czego używacie w testach integracyjnych, MockMVC czy WebTestClienta? I czy w ogóle można używać w testach integracyjnych MockMVC, bo słyszałem już różne opinie.

0

Test który strzela pod endpoint to nie koniecznie jest test integracyjny (w przypadku amatorskich projektów, najprawdopodobniej nie jest). Twój interfejs http może być jednostką którą możesz testować, i możesz w takich testach testować endpointy.

To powiedziawszy, Twoje testy mogą korzystać z albo z abstrakcji na endpointy, albo na prawdę wykonać request http, albo jak chcesz inaczej. Tak na prawdę, to nie ma znaczenia czy strzelisz z mock-mvc czy web-test.

0
Riddle napisał(a):

Test który strzela pod endpoint to nie koniecznie jest test integracyjny (w przypadku amatorskich projektów, najprawdopodobniej nie jest). Twój interfejs http może być jednostką którą możesz testować, i możesz w takich testach testować endpointy.

To powiedziawszy, Twoje testy mogą korzystać z albo z abstrakcji na endpointy, albo na prawdę wykonać request http, albo jak chcesz inaczej. Tak na prawdę, to nie ma znaczenia czy strzelisz z mock-mvc czy web-test.

No ja mam na myśli takie testy, które uderzają do endpointa i przechodzą przez całą logikę

1
Nofenak napisał(a):

No ja mam na myśli takie testy, które uderzają do endpointa i przechodzą przez całą logikę

No to to nie jest test integracyjny.

I odpowiadając - test powinien być napisany w taki sposób, żeby nie wiedział czy korzysta z MockMvc czy WebTest, to powinno być schowane za abstrakcją. Więc odpowiadając na Twoje pytanie - nie ma znaczenia czego użyjesz.

0
Riddle napisał(a):
Nofenak napisał(a):

No ja mam na myśli takie testy, które uderzają do endpointa i przechodzą przez całą logikę

No to to nie jest test integracyjny.

I odpowiadając - test powinien być napisany w taki sposób, żeby nie wiedział czy korzysta z MockMvc czy WebTest, to powinno być schowane za abstrakcją. Więc odpowiadając na Twoje pytanie - nie ma znaczenia czego użyjesz.

Eh, ale nawet jak ten test będzie miał jakieś abstrakcje, to czegoś pod tą abstrakcją trzeba użyć

0
Nofenak napisał(a):

Eh, ale nawet jak ten test będzie miał jakieś abstrakcje, to czegoś pod tą abstrakcją trzeba użyć

Wybierz sobie co Ci bardziej pasuje, nie ma znaczenia.

0

Używam MockMVC, do tego służy to narzędzie.

0
Majksu napisał(a):

Używam MockMVC, do tego służy to narzędzie.

Żaden argument. jQuery służy do obsługi UI, nie znaczy że warto go używać.

1

Wiremock i RestAssured

0

Pytasz o Y, które brzmi: Czy jest sens podnoszenia całego serwera http do testów?
Miałeś cały wątek o MockMVC, tuż po wspaniałym feedbacku rekrutacyjnym 😉

Odp: Można, pułapka na którą musisz uważać: mocki są mylne, bo gdy coś się zmieni w systemie, to zazwyczaj mock pozostaje i test jest dalej zielony.
Z Wiremockiem masz tylko jedno miejsce prawdy.

Btw. Definicja testów integracyjnych jest szeroka, zależy, która Ci się podoba. Jak dla mnie odpalenie całego kontestu Spinga z bazą jest czymś więcej niż odpaleniem JUnitowego runnera.

0
Productionserver napisał(a):

Pytasz o Y, które brzmi: Czy jest sens podnoszenia całego serwera http do testów?
Miałeś cały wątek o MockMVC, tuż po wspaniałym feedbacku rekrutacyjnym 😉

Odp: Można, pułapka na którą musisz uważać: mocki są mylne, bo gdy coś się zmieni w systemie, to zazwyczaj mock pozostaje i test jest dalej zielony.
Z Wiremockiem masz tylko jedno miejsce prawdy.

+1.

Lepiej odpalić kontroler w teście niż mocka.

Productionserver napisał(a):

Btw. Definicja testów integracyjnych jest szeroka, zależy, która Ci się podoba.

Powiedziałbym że jest dużo mylnych interpretacji tej dosyć prostej idei.

Productionserver napisał(a):

Jak dla mnie odpalenie całego kontestu Spinga z bazą jest czymś więcej niż odpaleniem JUnitowego runnera.

To coś jest nie tak, bo to jest szczegół implementacyjny. Nie ma znaczenia czym odpalasz testy, albo co one podnoszą - ma znaczenie co testują.

0
Productionserver napisał(a):

Dałem Ci już 2 powody. Idąc Twoim tropem można nawet napisać test e2e, który testuje jedną konkretną rzecz i mówić, że jest jednostkowym. Test integracyjny jest czymś więcej niż test jednostkowy, który piszę na poziomie klas/metod. Z chęcią się dowiem jak Ty definiujesz test integracyjny. Zależności do innych serwisów niezależnych od nas? Często zawodzą, wolę testować wtedy kontrakty.

Idąc Twoim tropem można nawet napisać test e2e, który testuje jedną konkretną rzecz i mówić, że jest jednostkowym zależy co testuje. Mam wrażenie że to Ci umyka, bo w swoich trzech ostatnich komentarzach mówisz cały czas o tym jak coś testujesz (stawia to, stawia tamto, jest szybki, jest wolny, jest zawodny, etc).

Po pierwsze - jeśli masz testy które zawodzą, to nie znaczy od razu że są integracyjne. Testy powinny być przewidywalne i powtarzalne. Jeśli masz test który "często zawodzi" to taki test się do niczego nie nadaje, i trzeba go albo poprawić tak żeby nie był zawodny (tzn. w 100% przypadków dawał ten sam wynik), albo usunąć. Nie ma miejsca w normalnych aplikacji na "zawodne testy". I nazwanie takich testów "integracyjne" nic nie daje, to jest raz.

Test integracyjny jest czymś więcej niż test jednostkowy, który piszę na poziomie klas/metod Pomysł że "test jednostkowy" to jest taki który pisze się na poziomie metod i klas, też moim zdaniem to jest słabe określenie, bo to sprawia że testy są przywiązane do implementacji, i ciężko je potem zmienić.

Nie wiem czy dobrze rozumiem, popraw mnie jeśli się mylę, ale wydaje mi się, że według Twoich definicji to:

  • test jednostkowy, to taki który operuje tylko na klasach bez side efektów, ewentualnie robi mocki, i nie stawia nic, i jest szybki (milisekundy)
  • test integracyjny, to taki który stawia ciężkie rzeczy, jak baze, kafke, etc. i jest wolny (sekundy do minut)
  • test e2e to taki który strzela od UI do bazy, i strzelam że pewnie też według Ciebie mogą trwać długo?

dobrze rozumiem?

Bo jeśli takie są Twoje definicje, to mam dla Ciebie bardzo złe wieści, bo wygląda że całkowicie Ci umknęły te nazwy, z kilku powodów. Po pierwsze dlatego że te definicje w żaden sposób nie opisują tego CO test testuje, tylko w jaki sposób, a to jest ogromna różnica. Nie pisze się testów po to żeby sprawdzić czy "kod który napisałem, to jest kod który napisałem", test ma sprawdzić zachowanie, czy program robi to co ma robić.

Pytałeś o moją definicję, to:

  • żaden test (nie ważne czy jednostkowy, czy integracyjny, czy e2e) nie może trwać długo (wszystkie powinny być błyskawicznie szybkie, nawet te które testują kafkę, bazę, czy cokolwiek innego)
  • żaden test (nie ważne czy jednostkowy, czy integracyjny, czy e2e) nie może być zawodny (czyli raz przejść raz nie przejść)
  • żaden test (nie ważne czy jednostkowy, czy integracyjny, czy e2e) nie może dawać false negative'ów (czyli przechodzić jak jest bug)
  • żaden test (nie ważne czy jednostkowy, czy integracyjny, czy e2e) nie może dawać false positive'ów (czyli failować jak nie ma buga)
  • każdy test (nie ważne czy jednostkowy, czy integracyjny, czy e2e) powinien dawać jednoznaczną odpowiedź: "okej" albo "nie okej".
  • każdy test (nie ważne czy jednostkowy, czy integracyjny, czy e2e) jak się go odpali kilka razy pod rząd, ma mieć taki sam wynik
  • każdy test (nie ważne czy jednostkowy, czy integracyjny, czy e2e) powinien być w miarę możliwości niezależy od swojego środowiska (czyli nie ważne czy się go odpali na lokalu, na dev/uat czy gdzie tam chcesz), czyli jeśli dany code base failuje albo przechodzi jakiś test na localu, to ten sam codebase na wszystkich innych środowiskach ma dać ten sam wynik (niezależnie czy unit, integ. czy e2e).
  • sumarycznie wszystkie testy jakie masz w aplikacji powinny Ci dać 100% coverage'a (100% jest nie osiągalne, ale 99% byłoby git).
  • każdy test (nie ważne czy jednostkowy, czy integracyjny, czy e2e) powinien mówić co testuje, czyli:
    • test 1 testuje dodawanie czegoś
    • test 2 testuje usuwanie czegoś
    • test 3 testuje permissiony
    • test 4 testuje to czy user nie zmienić avatara cześciej niż raz dziennie, czy co tam.
  • każdy test (nie ważne czy jednostkowy, czy integracyjny, czy e2e) może sobie "pod spodem" korzystać z czego chce (kafka, baza, docker, mocki, co chce), tak długo jak są szybkie, powtarzalne, nie są zawodne, i testują jakieś zachowanie systemu.

Jeśli masz testy integracyjne albo e2e które są zawodne, to są po prostu słabe i musisz je poprawić. Jeśli masz testy które są "integracyjne", bo stawiają kafke albo baze i to trwa długo, to to jest po prostu słaby test bo Cię spowalnia, i należy tak zrobić żeby je przyspieszyć.

Co o czym Ty mówisz, typu "jeśli test odpala kafke to jest integracyjny, a jak odpala ui to jest e2e", to jest przypisywanie "typów testów" zależnie od tego czego one sobie pod spodem używają, a to nie ma sensu.

0
Productionserver napisał(a):

Sugerujesz mi, że moje testy są gorszej jakości, bo używam innej definicji.. komiczne

To jakiej używasz definicji, daje mi powody przypuszczać że gdybym zobaczył Twoje testy to bym się przeraził.

Productionserver napisał(a):

Nadal mi nie odpowiedziałeś, czym są dla Ciebie testy integracyjne?

Test integracyjny, to jest po prostu test który testuje integracje pomiędzy dwoma elementami systemu (ale integrację, a nie zachowanie). I nie, podnoszenie kafki albo bazy to nie jest testowanie integracji.

Jeśli test testuje integrację, to jest integracyjny. Jeśli nie testuje integracji, to nie jest. Proste. Przy czym kluczem jest "testuje integracje", a nie "korzysta z integracji".

Podobnie: jeśli test testuje jednostkę (np moduł), to jest jednostkowy. Jeśli nie testuje jednostki (tylko np integrację), to nie jest. To miałem na myśli kiedy mówiłem, że ma znaczenie co testujesz, a nie jak.

Jeśli test jest wolny, zawodny, albo failuje bez powodu, to mam dla niego specjalną kategorię: "shit test".

Productionserver napisał(a):

"bo to sprawia że testy są przywiązane do implementacji" -> dlatego to jest najniższy poziom w piramidzie. Testujesz abstrakcję na poziomie implementacji?

yyy... raczej dlatego te testy są gorszej jakości, bo utrudniają refaktor i zmianę kodu. Test który jest w taki sposób "przywiązany do implementacji" nie jest jednostkowy. To jest po prostu słaby test, którego w ogóle nie powinno być. Testy (również jednostkowe) mają testować zachowanie, to co kod robi - nie to jak jest napisany.

Productionserver napisał(a):

"sumarycznie wszystkie testy jakie masz w aplikacji powinny Ci dać 100% coverage'a" -> settery, gettery, klasy konfiguracyjne i cały boilerplate też testujesz?

Kolejny symptom.

Jeśli chodzi o settery, gettery, konfiguracje i inne takie rzeczy, które z jakiegoś powodu wydają Ci się nie warte testowania, to:

  • albo są konieczne, i muszą być w kodzie - (to wtedy tak, trzeba jest przetestować, zostanie nieprzetestowanego kodu jest nieodpowiedzialne)
  • albo (z reguły) nie są konieczne, i ludzie je dodają po nic. wtedy warto je po prostu usunąć, i wtedy nie musisz ich testować.
Productionserver napisał(a):

Daj mi ten przepis na szybkie testy e2e, proszę. (i nie mówię tu o JSowych unitach)

Okej, czyli tak jak myślałem. Sporo ludzi ma problem takimi teststami, bo jak się je napisze na pałe (albo np idąc za tutorialem) to są wolne, i zawodne, i wtedy zamiast je poprawić, tak żeby były rzetelne i szybkie, to ludzie je po prostu nazywają "e2e test" albo "integration test" i zostawiają. Tymczasem test który jest wolny i zawodny jest bardzo zły, nie ważne jak go sobie nazwiesz.

Nie ma jednego "przepisu" na szybki test. Po prostu trzeba znaleźć i usunąć bottlenecki. Odpal profiler, albo debugger, znajdź miejsce które spowalnia, i napisz test tak, żeby nadal testował to co ma testować (ale co, nie jak), usuwając bottlenecki, i tyle. Jest to trudne, owszem - ale to jest to co jest wymagane do tego żeby mieć dobre testy, nie ma nic za darmo. Jak tak zrobisz kilka razy, to następne próby już będą łatwiejsze, najtrudniej jest zrobić pierwszych kilka kroków. Myślę że jak napiszesz swoje trzy pierwsze takie testy które trwają milisekundy (np 10-100ms), to zobaczysz że to jest jaknajbardziej wykonalne.

1

Test integracyjny, to jest po prostu test który testuje integracje pomiędzy dwoma elementami systemu (ale integrację, a nie zachowanie). I nie, podnoszenie kafki albo bazy to nie jest testowanie integracji.

Mam kawałek kodu, który publikuje coś na kafkę. Chcę przetestować jego zachowanie. Piszę test, w którego setupie odpalam kafkę w jakimś testcontainerze. Odpalam test, wszystko jest na zielono. Czy mając te informacje jesteś w stanie stwierdzić, czy integracja między moim kodem a kafką działa poprawnie?

0
hawus napisał(a):

Mam kawałek kodu, który publikuje coś na kafkę. Chcę przetestować jego zachowanie. Piszę test, w którego setupie odpalam kafkę w jakimś testcontainerze. Odpalam test, wszystko jest na zielono. Czy mając te informacje jesteś w stanie stwierdzić, czy integracja między moim kodem a kafką działa poprawnie?

Musiałbym zobaczyć ten test, dużo zmiennych tutaj jest. Poza tym, to ma znaczenie co wrzucasz na tą kafkę, i po co. To nie jest tak że to "publikuje coś" wrzucasz tylko po to żeby było, tylko chcesz coś tym osiągnąć. Np wrzucasz event o wiadomości na topic kafkowy, bo chcesz dać znać innemu użytkownikowi że przyszła wiadomość. Sam fakt że nie napisałeś co wrzucasz i po co, to jest zły symptom. To jest ten sam problem co @Productionserver ma, czyli mówisz o jak, a nie co - "publikuje na kafke", "odpalam test container", "odpalam kafke" - to jest wszystko szczegół implementacyjny testu, i nie mówi nic o tym co jest na prawdę testowane.

0
Riddle napisał(a):
hawus napisał(a):

Mam kawałek kodu, który publikuje coś na kafkę. Chcę przetestować jego zachowanie. Piszę test, w którego setupie odpalam kafkę w jakimś testcontainerze. Odpalam test, wszystko jest na zielono. Czy mając te informacje jesteś w stanie stwierdzić, czy integracja między moim kodem a kafką działa poprawnie?

Musiałbym zobaczyć ten test, dużo zmiennych tutaj jest. Poza tym, to ma znaczenie co wrzucasz na tą kafkę, i po co. To nie jest tak że to "publikuje coś" wrzucasz tylko po to żeby było, tylko chcesz coś tym osiągnąć. Np wrzucasz event o wiadomości na topic kafkowy, bo chcesz dać znać innemu użytkownikowi że przyszła wiadomość. Sam fakt że nie napisałeś co wrzucasz i po co, to jest zły symptom. To jest ten sam problem co @Productionserver ma, czyli mówisz o jak, a nie co - "publikuje na kafke", "odpalam test container", "odpalam kafke" - to jest wszystko szczegół implementacyjny testu, i nie mówi nic o tym co jest na prawdę testowane.

void myTest() {
  const service = setUpService(); // to stawia service ze wszystkimi zależnościami, w szczególności kafkę w w testcontainerze
  service.addEntry({topic: "foobar", content: "lorem ipsum"}); // wywołuję coś na serwisie, celem ma być wysłanie maila zainteresowanym użytkownikom zainteresowanym tematem "foobar"
  assertNotificationsSent({topic: "foobar"}); // sprawdzam, czy każdy zainteresowany tematem foobar dostał powiadomienie
}

Czy patrząc na taki test, wiedząc, że

  • setUpService stawia testcontainer z kafką i zwraca instancję serwisu podpiętego do tej kafki,
  • service.addEntry() publikuje coś na kafkę,
  • assertNotificationsSent sprawdza, czy komponent subskrybujący kafkę wysłał powiadomienia do odpowiednich użytkowników,

mogę stwierdzić, że integracja z kafką działa, przynajmniej w zakresie, jakiego potrzebuję?

0
hawus napisał(a):

Czy patrząc na taki test, wiedząc, że

  • setUpService stawia testcontainer z kafką i zwraca instancję serwisu podpiętego do tej kafki,
  • service.AddEntry() publikuje coś na kafkę,
  • assertNotificationsSent sprawdza, czy komponent subskrybujący kafkę wysłał powiadomienia do odpowiednich użytkowników,

mogę stwierdzić, że integracja z kafką działa, przynajmniej w zakresie, jakiego potrzebuję?

Wygląda prawie że spoko, ale niepotrzebnie zna szczegół implementacyjny.

Jak czytam

Ja bym go napisał bardziej tak:

void notifyInterestedUsers() {
  var x = ; // tym "x", powinno być coś, po czym można wykminić którzy userzy mają być powiadomieni, raczej nie sprawdzałbym tego po topicu kafkowym.

  // np. var x = usersWithSubscriptionOn("/Forum/Java");
  
  notifyInterestedUsers("Lorem ipsum", x);
  assertUsersNotified(x);
}
hawus napisał(a):

mogę stwierdzić, że integracja z kafką działa, przynajmniej w zakresie, jakiego potrzebuję?

A czemu myślisz że potrzebujesz wiedzieć że "integracja z kafką działa"? Czemu nie wystarczy Ci wiedzieć że użytkownicy którzy mieli dostać powiadomienie dostali? I.e. czemu test sprawdzający powiadomienia ma cokolwiek wiedzieć o tym jakim kanałem te powiadomienia lecą? Czy jak zrefaktorujesz notyfikację z kafki na innego message brokera, to czy ten test przestanie być prawidłowy? Albo jak to przerobisz na dowolny inny sposób dostarczania powiadomień? Oczywiście nie przestanie, więc nie powinien zależeć od kafki w taki sposób.

User nie potrzebuje żeby "kafka działała", user potrzebuje dostać powiadomienie. To że to powiadomienie leci kafką, to jest tylko szczegół implementacyjny. Oczywiście musisz zadbać, żeby user dostał to powiadomienie, musisz się upewnić że ono doszło, ale nie koniecznie musisz przywiązać swój test do kafki żeby to zrobić. Otrzymanie powiadomienia to jest bezpośredni cel, bezpośrednie zachowanie (co) które chcesz przetestować. To że to leci po kafce, to jest jak, i o tym test nie powinien wiedzieć. Oczywiście kafka musi być uruchomiona w teście, ale "pod spodem".

Pod co powinieneś pisać testy:

  • że powiadomienie wyszło
  • że dotarło
  • że dotarło do tych do których miało
  • że nie zginęło
  • że jak jest błąd na sieci albo jak serwis padł, to że powiadomienie jest ponowione
  • że nie zostało odebrane dwa razy
  • że połączenie z serwisem które je propaguje, jest utrzymane
  • pod cokolwiek innego czego użytkownik wymaga od systemu, np że powiadomienie doszło do jego kilku urządzeń

Pod co nie powinieneś pisać testów

  • pod konkretny topic
  • pod nic związanego konkretnie z kafką
0
Riddle napisał(a):
hawus napisał(a):

mogę stwierdzić, że integracja z kafką działa, przynajmniej w zakresie, jakiego potrzebuję?

A czemu myślisz że potrzebujesz wiedzieć że "integracja z kafką działa"? Czemu nie wystarczy Ci wiedzieć że użytkownicy którzy mieli dostać powiadomienie dostali?

Wystarczy mi to (i ogólnie zgadzam się z resztą twojego posta), ale całe clou w tym, że jeśli mam pod spodem tę kafkę, jak bardzo ukryta ona by nie była, to jednak wciąż będzie i, chcąc nie chcąc, każdy test, który na niej polega, będzie testem integracyjnym, bo poza tym, co jest testowane explicit, testowana jest też integracja z kafką. Jeśli logika jest skopana - test nie przejdzie. Jeśli integracja jest skopana - test też nie przejdzie. Jeśli zmienię kafkę na coś, z czym nie muszę się integrować (bo np. jest elementem języka lub platformy, tak jak genserver w OTP albo channel w Go) to ten test przestanie być integracyjny. Ale póki jego działanie zależy od poprawnej integracji z czymkolwiek (w tym wypadku z kafką) to nieważne jak bardzo ta integracja i kafka nie będą ukryte za warstwami abstrakcji, będzie to test integracyjny.

Wychodząc z drugiej strony: mając już taki test jak napisałeś

void notifyInterestedUsers() {
  var x = ; // tym "x", powinno być coś, po czym można wykminić którzy userzy mają być powiadomieni, raczej nie sprawdzałbym tego po topicu kafkowym.

  // np. var x = usersWithSubscriptionOn("/Forum/Java");
  
  notifyInterestedUsers("Lorem ipsum", x);
  assertUsersNotified(x);
}

wiedząc, że pod spodem lecą jakieś eventy do kafki i uruchamiają się jacyś konsumenci, napisałbyś kolejny test integracyjny (wedle tej definicji: Test integracyjny, to jest po prostu test który testuje integracje pomiędzy dwoma elementami systemu (ale integrację, a nie zachowanie), który sprawdza poprawność integracji z kafką?

0
hawus napisał(a):

Wystarczy mi to (i ogólnie zgadzam się z resztą twojego posta), ale całe clou w tym, że jeśli mam pod spodem tę kafkę, jak bardzo ukryta ona by nie była, to jednak wciąż będzie i, chcąc nie chcąc, każdy test, który na niej polega, będzie testem integracyjnym,

Czyli mam rozumieć, że dla Ciebie "test integracyjny" to dowolny test, który w ramach działania dotyka jakiejkolwiek zależności?

hawus napisał(a):

bo poza tym, co jest testowane explicit, testowana jest też integracja z kafką. Jeśli logika jest skopana - test nie przejdzie. Jeśli integracja jest skopana - test też nie przejdzie.

I tu też się mylisz, bo nie jest. To że test używa czegoś, to nie znaczy że testuje coś.

Testy korzystają z wielu różnych rzeczy, ale idealnie powinny testować tylko jedną. Każdy jeden test na świecie, przechodzi przy jakimś założeniu (założeniu ustalanym przez poprzednie testy). Nie wiem czy da się napisać test który zależy od niczego (chyba że pierwszy test w projekcie). Więc to jest naturalne, że napiszesz test który może nie przejść z kilku powodów (bo, tak jak mówisz że albo logika jest skopana, albo gadanie z kawką jest skopane). To jest prawda - ale to nie oznacza że test testuje te rzeczy.

Ten przykład który podałeś, z powiadomieniami (zachowanie biznesowego) przez kafkę (szczegół implementacyjny), to jest prawda że może nie przejść jak coś się stanie z kafką - ale to nie znaczy że ten test testuje kafke.

2

Tak niestandardowo zapytam. Jaką różnice robi, czy test zna szczegóły implementacyjne, czy nie? Test ma tylko sprawdzić, czy coś działa i na tym kończy się jego zadanie.

1

Drugie pytanie odnośnie czasu egzekucji testu. Unit powinien być szybki - pełna zgoda. Można sobie coś zamokować, obejść i puścić 1000 testów w kilka sekund. Ale e2e? Co w przypadku testowania integracji apki mikroserwisowej składającej się z x serwisów i kilku rabbitów / event hubów (albo kafek) / storydzy itp pomiędzy. A niech jeszcze wszystko jeszcze stoi na AKS albo innej kedzie. Wszystkie te toole same w sobie mają pewne czasowe ograniczenia i deleye i siłą rzeczy nie da się tego przyspieszyć. Możemy sobie zamokować część ścieżki w celu przyspieszenia, ale czy nadal to będzie pełno prawna "integracja"?

0

@ledi12 : zaraz będzie za dużo na stole...
Ridlowy test, musi być wręcz "atomowy", bo inaczej to nic nie testuje

testuje się wszelako, bo na tym polegają testy. Unitowe są od samego developera, IMO każdy dev potrafi coś takiego zrobić. A jak testujesz kontroler i repo, to zrób taki test, jaki kłopot?
a jak testujesz kontroler, serwis, repo i np. Kafkę? Taki jest test case, i to się normalnie robi.
😄

6

Hej, panowie, to maiał być temat o WebClient vs MockMVC a nie ogólna dyskusja o testach xd

0
ledi12 napisał(a):

Drugie pytanie odnośnie czasu egzekucji testu. Unit powinien być szybki - pełna zgoda.

Każdy test powinien być szybki.

ledi12 napisał(a):

Można sobie coś zamokować, obejść i puścić 1000 testów w kilka sekund. Ale e2e? Co w przypadku testowania integracji apki mikroserwisowej składającej się z x serwisów i kilku rabbitów / event hubów (albo kafek) / storydzy itp pomiędzy.

Trzeba kombinować, żeby to przyspieszyć. Albo podzielić na kawałki, albo usunąć niepotrzebne rzeczy, albo dodać cache, albo zastosować jeden z milionów tricków które się stosuje normalnie w developmencie.

Tak, jest to mozolne. Jest to trudne. Wymaga debugowania i profilowania, żeby znaleźć te bottlenecki, często w dokumentacji nie jest opisane jak przyspieszać testy. Tak. Często tutoriale same w sobie piszą "running x tests might be slow". Dodatkowo, często ops'i mówią żeby odpalać testy na jenkinsach zamiast na localu, bo są wolne, a to tym bardziej wydłuża feedback loop, co daje "pretekst" programistom do pisania wolnych testów. Również często sam nasz design aplikacji jest bezpośrednim źródłem czemu testy są wolne.

Ale jak najbardziej się da je przyspieszyć jak się do tego usiądzie.

ledi12 napisał(a):

A niech jeszcze wszystko jeszcze stoi na AKS albo innej kedzie. Wszystkie te toole same w sobie mają pewne czasowe ograniczenia i deleye i siłą rzeczy nie da się tego przyspieszyć.

No to trzeba przenieść jak najwięcej testów się da na lokalne środowisko, a na AWS zostawić tylko i wyłącznie absolutne minimum.

ledi12 napisał(a):

Możemy sobie zamokować część ścieżki w celu przyspieszenia, ale czy nadal to będzie pełno prawna "integracja"?

Integracje się testuje osobno, a zachowanie osobno. Miej jeden test integracyjny z prawdziwą ścieżką, a pozostałe niech testują zachowanie ze zmockowaną ścieżką.

ale czy nadal to będzie pełno prawna "integracja"? Bardzo prosto się możesz o tym przekonać. Zakomentuj w kodzie lub usuń jakiś fragment, który wymaga tej integracji. Testy failują? To jest "pełnoprawna integracja". Żaden test nie wykrył tego buga? Testy zepsute.

To przekonanie że "testy muszą być wolne" to jest jakaś zaraza w naszej branży. Jak masz wolne testy, to przestają Ci pomagać, i zaczynają przeszkadzać.

0
Riddle napisał(a):
hawus napisał(a):

Wystarczy mi to (i ogólnie zgadzam się z resztą twojego posta), ale całe clou w tym, że jeśli mam pod spodem tę kafkę, jak bardzo ukryta ona by nie była, to jednak wciąż będzie i, chcąc nie chcąc, każdy test, który na niej polega, będzie testem integracyjnym,

Czyli mam rozumieć, że dla Ciebie "test integracyjny" to dowolny test, który w ramach działania dotyka jakiejkolwiek zależności?

Nie jakiejkolwiek, ale ściśle zdefiniowanej - takiej, z którą:

  • zachodzi komunikacja międzyprocesowa - między tą zależnością a moją aplikacją,
  • nie kontroluję jednej (lub obu) ze stron read/write.

Mam tez inną definicję, ale ona pewnie wywoła jeszcze więcej zamieszania - test integracyjny to taki, który uruchamia więcej niż jeden nietrywialny fragment kodu :-)

RAM nie jest zależnością w sensie testu integracyjnego, bo mój kod kontroluje co i jak jest zapisywane i odczytywane. Kafka jest, bo mój kod musi się dostosować do tego, jak kafka działa i jednocześnie nie ma nad tym żadnej kontroli.

Riddle napisał(a):
hawus napisał(a):

bo poza tym, co jest testowane explicit, testowana jest też integracja z kafką. Jeśli logika jest skopana - test nie przejdzie. Jeśli integracja jest skopana - test też nie przejdzie.

I tu też się mylisz, bo nie jest. To że test używa czegoś, to nie znaczy że testuje coś.

Moim zdaniem test integracyjny to taki, który do poprawnego wykonania wymaga poprawnej integracji z czymś. Twoim zdaniem (popraw mnie jeśli się mylę), test integracyjny to taki, który explicit testuje integrację. BTW w przypadku tej kafki ja bym trzymał oba testy - jeden testujący integrację explicit, a drugi testujący zachowanie. Ale jeśli ten drugi używa "prawdziwej" kafki, to oba nazwałbym integracyjnymi. Chociaż może trafniejszym terminem byłoby "zintegrowane" niż "integracyjne".

0
hawus napisał(a):

BTW w przypadku tej kafki ja bym trzymał oba testy - jeden testujący integrację explicit, a drugi testujący zachowanie. Ale jeśli ten drugi używa "prawdziwej" kafki, to oba nazwałbym integracyjnymi. Chociaż może trafniejszym terminem byłoby "zintegrowane" niż "integracyjne".

Tak, powinieneś mieć oba testy, ale osobno. Jeden testujący wymagane zachowanie systemu (np wysyłanie powiadomień), jeden testujący połączenie z message brokerem (nie koniecznie wiedzący czy to jest kafka czy nie).

Ale jeśli ten drugi używa "prawdziwej" kafki, to oba nazwałbym integracyjnymi. Ale po co Ci to? To jest szczegół implementacyjny. Mam wrażenie, że próbujesz nazwać test który stawia kafkę "integracyjny", bo wewnętrznie czujesz że on jest bardziej zawodny i może sfailować z randomowego powodu. Albo może czujesz jakąś potrzebę rozróżnienia testów które uważasz za pewniak, i testów które uważasz za nie pewne. Czujesz, że jeśli test korzysta z "prawdziwej kafki", to jest większa szansa że randomowo sfailuje, więć musisz myśleć o tych testach w inny sposób, żeby móc nad nimi pracować - i dlatego pasuje Ci kategoryzowanie ich na "jednostkowe" i "integracyjne". Tylko że to jest całkowite błędne podejście, bo posiadanie takich testów które randomowo failują jest trudne w utrzymaniu oraz nie pomagają w developmencie, tylko przeszkadzają. Za każdym razem jak taki głupi test sfailuje, to to jest kolejna rzecz o której trzeba pamiętać i z tym handlować. Taki test zaczyna być obciążeniem, a nie pomocą. Ile razy byłeś spowolniony przez taki randomowy fail jakiegoś testu, który potem odpalasz i przechodzi? To spowalania i przeszkadza w pracy.

Powinieneś tak przerobić te testy żeby każdy z nich (nie ważne czy używa prawdziwej kafki czy nie), zawsze przechodził (albo failował), i był całkowicie niezawodny, zawsze dawał ten sam wynik. Innymi słowy, jak masz jakiś test, który Ty nazywasz "integracyjny", bo np raz na 5 odpaleń failuje, to popraw go tak żeby miał wszystkie te cechy które mają test jak Ty mówisz "jednostkowe", zrób żeby był szybki, i zrób żeby jego wynik był w 100% deterministyczny. Owszem - może to być trudne, może to wymagać zabawy z ustawieniami systemowymi, zabawy ze zmiennymi środowiskowymi, całą masą śmieci, bo takie rzeczy jak kafka lubią mieć tight-coupling na system, więc są uciążliwe do setupowania - ale da się to zrobić, i jak to już zrobisz, to Twoja praca z takimi kodem przyspieszy znacznie, gwarantuje Ci.

Jak zadbasz o porządne testy (czyli takie że każdy jeden test jest szybki, i każdy jeden test zawsze daje ten sam wynik), to zaczniesz zauważać że kafka to na prawdę jest szczegół implementacyjny, i możesz nim dowolnie żonglować. Wtedy takie kategoryzowanie "wolny test"/"szybki test" przestaje mieć sens, bo każdy test jest szybki. Twoja praca wtedy przyspiesza, i dużo większe znaczenie ma co test testuje, a nie jak. I od tego "co" zależy czy test jest integracyjny czy nie. Jeśli testuje integrację to jest, jeśli nie, to nie jest.

0
ledi12 napisał(a):

Tak niestandardowo zapytam. Jaką różnice robi, czy test zna szczegóły implementacyjne, czy nie? Test ma tylko sprawdzić, czy coś działa i na tym kończy się jego zadanie.

Niby tak, ale nie wiem czy tylko.

Dwa powody.

  • Po pierwsze, im więcej szczegółów implementacyjnych zna test, tym większa szansa że zacznie failować jak zrobimy refaktor (więc ludzie nie robią refaktorów, żeby nie zepsuć testów).
  • Po drugie, im więcej szczegółów implementacyjnych, tym mniejsza abstrakcja w testach, ergo ciężej je zrozumieć, ergo ciężej je pisać, utrzymać i poprawić (więc ludzie nie będą ich pisać, utrzymywać i poprawiać należycie, bo jest to wtedy trudne).
0
Riddle napisał(a):

Powinieneś tak przerobić te testy żeby każdy z nich (nie ważne czy używa prawdziwej kafki czy nie), zawsze przechodził (albo failował), i był całkowicie niezawodny, zawsze dawał ten sam wynik. Innymi słowy, jak masz jakiś test, który Ty nazywasz "integracyjny", bo np raz na 5 odpaleń failuje, to popraw go tak żeby miał wszystkie te cechy które mają test jak Ty mówisz "jednostkowe", zrób żeby był szybki, i zrób żeby jego wynik był w 100% deterministyczny. Owszem - może to być trudne, może to wymagać zabawy z ustawieniami systemowymi, zabawy ze zmiennymi środowiskowymi, całą masą śmieci, bo takie rzeczy jak kafka lubią mieć tight-coupling na system, więc są uciążliwe do setupowania - ale da się to zrobić, i jak to już zrobisz, to Twoja praca z takimi kodem przyspieszy znacznie, gwarantuje Ci.

To nie jest trudnie, to jest fizycznie niemożliwe. Jak mam zrobić, żeby test polegający na czymś, nad czym nie mam kontroli, zawsze był zielony? To, że go nazwę jednostkowym a nie integracyjnym nic nie zmieni. Nawet taki najczystszy, jednostkowy (wg wszelkich definicji) test może się wykrzaczyć przez bit flip, a co dopiero test, który polega na komunikacji międzyprocesowej i zewnętrznym systemie. Karta sieciowa odmówi posłuszeństwa. Zewnętrzny system ma memory leaka, albo działa inaczej jak jest pełnia. Nie jesteś w stanie tego przewidzieć.

0
hawus napisał(a):

To nie jest trudnie, to jest fizycznie niemożliwe. Jak mam zrobić, żeby test polegający na czymś, nad czym nie mam kontroli, zawsze był zielony? To, że go nazwę jednostkowym a nie integracyjnym nic nie zmieni. Nawet taki najczystszy, jednostkowy (wg wszelkich definicji) test może się wykrzaczyć przez bit flip, a co dopiero test, który polega na komunikacji międzyprocesowej i zewnętrznym systemie.

Jak najbardziej jest to wykonalne i możliwe, tylko po prostu jest trudne. Twoje przekonanie o tym że tego się nie da zrobić, to jest to co Cię blokuje.

To, że go nazwę jednostkowym a nie integracyjnym nic nie zmieni. Nie powiedziałem Ci żebyś "nazwał go jednostkowym". Napisałem, żebyś: popraw go tak żeby miał wszystkie te cechy które mają test jak Ty mówisz "jednostkowe", zrób żeby był szybki, i zrób żeby jego wynik był w 100% deterministyczny..

hawus napisał(a):

Jak mam zrobić, żeby test polegający na czymś, nad czym nie mam kontroli, zawsze był zielony?

Bardzo trafne pytanie!

Odpowiedź:

  • Jeśli masz nad czymś kontrolę, i zmieniasz to - to powinieneś całkowicie to przetestować, i tutaj w 100% jesteś w stanie osiągnąć deterministyczność. Jeśli coś jest nie tak, w miejscu nad którym masz kontrolę, to testy powinny zawsze sfailować, dopóki tego nie naprawisz.
  • Jeśli nie masz nad czymś kontroli, np zewnętrzny system - to nie testujesz tego. Testujesz tylko komunikację Twojego programu z tym czymś, i na to też są różne sposoby. Jeśli nie masz kontroli nad tym czymś, i to coś np przestaje działać, to Twoje testy nie powinny się wywalić - bo i tak nie możesz tego naprawić. Jedyne co możesz zrobić to ewentualnie jakoś obsłużyć ten fail, i napisać sobie test: "jeśli zewnętrzny serwis daje mi connection refused, to mój program ma zrobić x".

Więc to są różne rodzaje integracji.

W momencie w którym piszesz @Test, i w którym uruchamiasz cokolwiek w testach, powinieneś sobie zadać pytanie: "jak mam jednoznacznie wprawić system pod testem w taki stan w jaki chcę" oraz potem "jak jednoznacznie sprawdzić czy po teście system jest w takim stanie jakim chcę". Musisz wtedy się zastanowić - czy to pod co piszę test, czy mam nad tym kontrolę czy nie? Jeśli mam, to napisz test w 100% deterministyczny. Jeśli nie, to nie testuj tego czegoś w ogóle (bo i tak nie możesz), a jedynie komunikację Twojego programu z tym czymś (i to też może być w 100% determinisnityczne). Jeśli orientujesz się że czasem Ci się uda czasem nie, to poniosłeś klęskę w napisaniu dobrego testu, i trzeba to jakoś naprawić.

Nie ma jednej odpowiedzi na to. Ale jak najbardziej się da tak napisać testy automatyczne żeby były deterministyczne. Testy automatycznę mogą sprawdzić wszystko to co człowiek może sprawić (w tym wypadku, wiadomo że nie ze wszystkim, np ui, ale to inny temat). Więc jeśli Ty sam jesteś w stanie sprawdzić manualnie czy program działa, to najpewniej test też jest w stanie to zrobić - tylko test musi dodatkowo kontrolować wszystkie zmienne (urle, porty, nazwy, ścieżki, etc.)

hawus napisał(a):

Karta sieciowa odmówi posłuszeństwa. Zewnętrzny system ma memory leaka, albo działa inaczej jak jest pełnia. Nie jesteś w stanie tego przewidzieć.

Ale nie testujesz zewnętrznego systemu, tylko Twoją integrację z zewnętrznym systemem.

tl;dr;

Test który randomowo failuje jest udręką, a nie pomocą. Nic Ci nie da test który odpala kafke, i jak go odpalisz to nie możesz mu ufać, i musisz go odpalać więcej razy. To zaczyna być obciążeniem.

Dobry test, ma Ci dać jednoznaczną odpowiedź na pytanie: "Działa" albo "nie działa". Jak odpalasz test, przechodzi i myślisz sobie "może działa", to taki test Ci jest w ogóle niepotrzebny.

0
Nofenak napisał(a):

Hej, panowie, to maiał być temat o WebClient vs MockMVC a nie ogólna dyskusja o testach xd

ok, to MockMVC styknie jak chcesz sobie potestować kontrolery i serwisy. Jest to GOTO w stacku Springowym, tak piszą w dokumentacji Springa, więc czemu im nie wierzyć? 🤷

tylko trzeba zwrócić uwagę na to, co testujesz. Jeśli jest to SpringMVC to używać trzeba MockMVC, a jeśli to jest Spring WebFlux to powinno się używać WebTestClient. To dość istotne, bo żadne mieszanki typu SpringMVC i WebTestClient, albo WebFlux i MockMVC po prostu nie zadziałają. Sam się na to ostatnio wpakowałem i nie polecam, do tej pory zbieram z podłogi wyrwane włosy...

A jeśli chcesz przeprowadzić test od kontrolera, przez serwis i repo/db to najlepszym rozwiązaniem będzie albo postawienie serwera, albo testcontainers (albo jedno i drugie jak potrzeba) i napisanie osobnego serwera/aplikacji, która będzie odpytywała Twój serwer. Tylko tyle i aż tyle.
W aplikacji testującej można sobie porobić normalne testy JUnitowe, które będą serwer testowany odpytywać. Zbierasz wyniki zusammen do kupy i masz konktretne wyniki. 😉

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