Jak testować, by nie strzelić sobie w kolano

Jak testować, by nie strzelić sobie w kolano
AF
  • Rejestracja: dni
  • Ostatnio: dni
0

@jarekr000000 O widzisz, piękna historia. Mógłbyś ją dodawać za każdym razem, gdy narzekasz na springa albo mocki, to wtedy ludzie wiedzieliby, w jakich systemach tkwisz od lat i że nie cały świat tak wygląda.

Tylko już gadamy o inżynierii oprogramowania, to trzeba myśleć szerzej. Piszesz Testowanie CI z prawdziwym oraclem ( u tego klienta) wiązałoby się z istotnie większym kosztem, przy niewielkim wzroście jakości testów. Olewam ten punkt - raczej na zawsze (zwłaszcza że moją misją na oracle jest `DROP DATABASE`). — dla Ciebie świat kręci się wokół oracla (którego sobie wygodnie emulujesz przez H2), ale wszystko dookoła poszło trochę naprzód, są inne bazy, inne technologie, inne podejścia. Z Twojego miejsca łatwo jest narzekać, ale większość świata jest teraz zupełnie gdzie indziej. A gadanie o misji DROP DATABASE jest dość ciekawe — jak ten oracle tak Ci przeszkadza, to zmień stos (albo pracę), spora część świata nie używa i pewnie nigdy nie używała tej bazy.

Krytykujesz mocki bo Cię kopnęły, bo nie testują "prawdziwej" interakcji z bazą danych i gdy wdrożyłeś, to się wysypało. No spoko, ale to nie jest problem z mockami, tylko z tym, że Ty nie masz porządnych testów na stosie produkcyjnym. Jak opierasz testowanie swojej aplikacji tylko o mocki, to oczywistym jest, że się to wysypie. Ale jak mockujesz sensownie (a nie assertWasCalled) i potem masz osobno zestaw testów bijących po prawdziwej bazie, to da się błędy wyłapać, nie trzeba stawiać żadnych dodatkowych klocków w kontenerach na localhoscie, na produkcję można pchać w piątek o 17, a i testy działają szybko. Tylko trzeba zmienić narrację z "mocki to zuo" i "robiłem takie rzeczy 20 lat temu, nie działało" na coś w stylu "jak nie mogę testować na prawdziwych komponentach produkcyjnych, to muszę znaleźć jakiś zamiennik do przetestowania każdego elementu systemu, bo na mockach nie testuję fragmentu interakcji DAL z jakąś bazą danych".

Testowanie z bazą danych, nie taka jak trzeba, na nie takim os, itd. daje mi znaczny wzrost jakości testów przy widocznym, ale nie tragicznym, wzroście kosztów — znowu, masz H2, które na Twoje potrzeby wystarczy. Ponawiam pytanie — co mam zrobić, jak moim produkcyjnym klockiem jest coś, czego nie da się emulować? Może to być baza danych, kolejka, szyna, API, system plików, mechaniczne ramię wpięte przez USB, nie ma to większego znaczenia. Jak testować, żeby nie strzelić sobie w kolano (bo potem będzie krzyk "mocki to zuo"), ale żeby też mieć sensowne testy i nie bać się pchać w piątek na produkcję, gdy potencjalny błąd będzie wymagał natychmiastowej reakcji (a nie w poniedziałek rano).

Reszta komentarzy raczej już poza głównym nurtem tematu.

jarekr000000 napisał(a):

Cała reszta aplikacji w tej fimie ma całkiem spoko SLA, jest środowisko przedprodukcyjne testowane głównie ręcznie (choć tam też działa Simulator).
Jeśli nie zadziała wydruk sald - to ludzie nie dostaną listów 2go tylko 7go w danym miesiącu - tragedii nie ma.
Punkty żegnaj się nie naliczą dziś - naliczą się jutro. Coś dwa razy zaksięgujemy - no to wyjdzie problem w jakimś bilansie w ciągu 48 godzin - zrobimy korektę.
Ogólnie luksusy.

Jasne, nie każda aplikacja musi działać niezawodnie. Są aplikacje online, są aplikacje offline, niektóre procesy muszą iść natychmiast, inne można ponowić kilka dni później, a raportowanie to pod koniec miesiąca. Tylko co to zmienia, jeżeli Ty stosujesz narrację "mocki to zuo, wszędzie, w każdej aplikacji"? To, że Ty masz ten komfort, że system nie wrzuci krytyka w piątek o 17 nie oznacza jeszcze, że inni taki komfort mają.

Tu jeszcze dodam może, że ogólnie testy to dość absurdalna metoda weryfikacji (słaba). Czasem jak pisze test to mam taki obrazek "matematyków" przed oczami: twierdzenie działa dla 1, działa dla 2 i działa dla 100 - czyli zachodzi dla każdego n.

Nie, nie jest absurdalna. Absurdalne byłoby stosowanie metod matematycznych, to strasznie zwolniłoby rozwój oprogramowania i świata, a do większości oprogramowania wystarczy przetestowanie ścieżek krytycznych i jakiś property based testing. Można bawić się w theorem provers, dependent types, jakieś idriso-podobne języki czy coś na ML, ale od pewnego poziomu nie daje to wielkiego zysku, za to strasznie spowalnia proces rozwijania kodu.

Jednak testy są po prostu ekonomiczne. Jak widać ekonomia widoczna jest również w samych testach - trzeba rozważyć koszty i dopasować do aplikacji/ SLA / potencjalnych szkód.

Ekonomia jest ważna wszędzie, jeżeli chcesz w ten sposób uzasadniać brak testowania na produkcyjnym stosie, to oczywiście nie ma problemu, ale to znacząco zmienia narrację (co skrzętnie pomijasz). A postawienie stosu produkcyjnego na boku może być bardzo drogie, gdy pracujesz z oraclem na jakimś mainframie, ale jak pracujesz z serwerem webowym na flocie 400 maszyn, to postawienie jeszcze jednej na testy nie jest problemem.

Przeważnie jest tak, że dośc tanio da się całkiem dużo przetestować, ale w miarę zwiększania jakości testów ich koszt zaczyna nagle rosnąć nieliniowo.

Znowu jakieś "liczby z powietrza". Ile to jest "przeważnie" i "dość tanio"? A ten koszt rośnie nieliniowo względem czego?

jarekr000000
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: U krasnoludów - pod górą
  • Postów: 4712
2

Wkraczasz już na ad personam i ad absurdum. - cofam to - może jestem przewrażliwiony

Po pierwsze, nie wszędzie mam Oracle - nigdzie tego nie pisałem.
Mam również całkiem zdrowe aplikacje, bez żadnych baz danych, albo używające je tylko w celu zrzucenia raportów.
Tylko, że nie ma wtedy o czym pisać - nie ma bazy - nie ma problemu.

Nie pisałem, że nie należy testować na produkcyjnym stosie :-( Tylko u mnie (ze wzgledu na to, że omijam startupy) taki stos to niestety częśto absurdalne rzeczy typu oracle, websphere mq itp.
Ciężko to zautomatyzować - przez to to nie są już testy integracyjne - tylko z mojego punktu widzenia manualne . To że są faktycznie zautomatyzowane niewiele zmienia - to nie jest wpięte w CI.
Feedback z takich testów dostaje za późno. U mnie, żeby zrobić pełne testy to trzeba wydrukować karty (a to wcale nie jest takie łatwe), podpiąc terminale płatnicze, skrzynki do kodowania PINów i inne cuda. Potem trzeba coś pokupować. Zrobić przelewy do/z banków. Po miesiącu sprawdzić wydruki sald. A po roku odpowiednie wpisy księgowe i podatkowe...
A jeszcze w międzyczasie sprawdzana jest zdolność kredytowa klienta. Jak się dobrze zastanowić to wyjdzie, ze muszę pół (małego) kraju podpiąć do środowiska testowego.
Żeby nie było - nikt żadnego roku, ani nawet tygodnia nie czeka, a pół kraju to nawet jest podłączone - prawie wszystkie firmy udestępniają swoje testowe środowiska /testowe api.

Nie traktuje to jako część swojego łańcucha testów.
Jak symulator (albo ręczne testy) wyłapie problem to traktuje to prawie jak błąd z produkcji - mam tylko więcej czasu na załatanie. Wcale nie jest spoko, bo może się okazać, że ten mój błąd - mimo że jest na środowisku testowym, to blokuje innym ludziom testy.
Ze wzgledu właśnie na ilość systemów nie mamy dużo takich środowisk, czasem prosty rollback do poprzedniej wersji nie jest taki prosty.

Pisałem, że mam też mocki (odziedziczone) - mimo, że mocki to zło, ale są to stosuję - nie są totalnie bezwartościowe, po prostu imo można pójść krok dalej. Widziałem zbyt dużo totalnie rozwalonych przez mocki testów. Sam zresztą miałem okres, że takie cuda robiłem - w zupełnie innych firmach i dawno. Nawet mnie to dziwi, ale już dawno nie trafiłem na prawdziwą mocksturbacje. Choć bym powiedział, że to raczej wynik mniejszej popularności mockito w obecnej okolicy niż świadoma decyzja.
I tak uważam, że dramat durnych mocków, które mnie nieraz kopły to wynik Springa i stosowania jego szalonego dependency injection - w tym sensie te mocki to nawet nie jest przyczyna zła, ale skutek.

Ponawiam pytanie — co mam zrobić, jak moim produkcyjnym klockiem jest coś, czego nie da się emulować? Może to być baza danych, kolejka, szyna, API, system plików, mechaniczne ramię wpięte przez USB, nie ma to większego znaczenia. Jak testować, żeby nie strzelić sobie w kolano (bo potem będzie krzyk "mocki to zuo")

Jadąc dalej ad absurdum - a co jak będziesz robił oprogramowenie do bomby termojądrowej - też będziesz za każdym razem odpalał testy na produkcyjnym stosie?

Co do kolejek, systemów plików to spokojnie takie rzeczy testuje - system plików testuje na systemie plików, a przy kolejkach bywa, że testuję na innych niż produkcyjny. (Bo produkcyjny jest websphere mq - straszna kupa).

Cała ta dyskusja imo sprowadza się do tego, że Ty uważasz, ze skoro i tak stestujesz pełny stos produkcyjny to możesz spokojnie robić mocki na baze danych w testach. OK.
Ja uważam, że skoro testowanie na stosie produkcyjnym jest trudne, długie i nie dostaje natychmiast wyniku to wolę testować już jakąś tam bazą danych. Nawet jeśli jest inna. Kosztuje mnie to więcej, ale niewiele więcej, a wynik mam szybciej. Przetestuję poprawność użytych SQL i DML, zmniejszam ryzyko mocksturbacji (overmockingu).

W niuanse związane z transaction isolation itd - raczej się nie ładuję. W jednej aplikacji gdzie takie niuanse były popisałem testy i działają tak samo na h2 jak na oracle (tam akurat można łatwo wymieniać).

Znowu jakieś "liczby z powietrza". Ile to jest "przeważnie" i "dość tanio"? A ten koszt rośnie nieliniowo względem czego?

Nieliniowo względem czasu pracy włożonego.
W pewnym momencie musisz włożyć dużo pracy, żeby polepszyć skuteczność testów, gdzie jest ten punkt zależy od stosu technologicznego i projektu.
Nie mogę podać liczb, bo oczywiście nie mierzyłem - to taka obserwacja. - oczywista - oczywistość.

A gadanie o misji DROP DATABASE jest dość ciekawe — jak ten oracle tak Ci przeszkadza, to zmień stos (albo pracę), spora część świata nie używa i pewnie nigdy nie używała tej bazy.

Już trochę baz wywaliłem. Więcej powstrzymałem (przed stworzeniem), Prace zmieniłem kilka razy :-) (2 razy - przez ostatnie 9 lat).
Żeby nie było - niedawno jeden schemat stworzyłem :-)

AF
  • Rejestracja: dni
  • Ostatnio: dni
1
jarekr000000 napisał(a):

Nie pisałem, że nie należy testować na produkcyjnym stosie :-( Tylko u mnie (ze wzgledu na to, że omijam startupy) taki stos to niestety częśto absurdalne rzeczy typu oracle, websphere mq itp.
Ciężko to zautomatyzować - przez to to nie są już testy integracyjne - tylko z mojego punktu widzenia manualne . To że są faktycznie zautomatyzowane niewiele zmienia - to nie jest wpięte w CI.
Feedback z takich testów dostaje za późno. U mnie, żeby zrobić pełne testy to trzeba wydrukować karty (a to wcale nie jest takie łatwe), podpiąc terminale płatnicze, skrzynki do kodowania PINów i inne cuda. Potem trzeba coś pokupować. Zrobić przelewy dop banków. Po miesiącu sprawdzić wydruki sald. A po roku odpowiednie wpisy księgowe i podatkowe...

O widzisz, tu jest chyba sedno.

Ze wzgledu właśnie na ilość systemów nie mamy dużo takich środowisk, czasem prosty rollback do poprzedniej wersji nie jest taki prosty.

No i to trzeba byłoby zwalczyć. Oczywiście wiem, że w finansach jakakolwiek ewolucja nie jest łatwa, ale niekoniecznie "mocki to zuo", tylko lepiej jest propagować podejście, w którym zrobienie infrastruktury jest proste i względnie szybie, dzięki czemu nawet jak się właduje w takie problemy jak Twoje, to jest jakiś sposób na wycofanie się z tego bez ubijania systemu.

I tak uważam, że dramat durnych mocków, które mnie nieraz kopły to wynik Springa i stosowania jego szalonego dependency injection - w tym sensie te mocki to nawet nie jest przyczyna zła, ale skutek.

Czyli znowu niekoniecznie mocki są złe, tylko mocki + DI + interfejs dla każdej klasy + inne rozwiązania enterprajs. Tylko że jak zmienisz jeden fragment tej układanki, to może się okazać, że wszystkie pozostałe są już okej.

Jadąc dalej ad absurdum - a co jak będziesz robił oprogramowenie do bomby termojądrowej - też będziesz za każdym razem odpalał testy na produkcyjnym stosie?

To nie jest ad absurdum, to jest normalne pytanie. Mam w pracy redshifta, którego nie dam rady niczym emulować (nawet postgresem, który rzekomo jest tym samym). Jak mam na nim testować?
Jak mam testować szynę enterprajs? Jak system kolejkowy? Jak powiadomienia z jakiegoś huba? Jak lambdę? To są normalne klocki, nie jakieś bomby. Odpowiadasz:

Co do kolejek, systemów plików to spokojnie takie rzeczy testuje - system plików testuje na systemie plików, a przy kolejkach bywa, że testuję na innych niż produkcyjny. (Bo produkcyjny jest websphere mq - straszna kupa).

A ja nie lubię tego podejścia, pewnie z podobnych pobudek. Nie uważam, żeby testowanie na zamiennikach było skuteczne, bo nieraz widziałem, że się sypie na prawdziwych.

Cała ta dyskusja imo sprowadza się do tego, że Ty uważasz, ze skoro i tak stestujesz pełny stos produkcyjny to możesz spokojnie robić mocki na baze danych w testach. OK.
Ja uważam, że skoro testowanie na stosie produkcyjnym jest trudne, długie i nie dostaje natychmiast wyniku to wolę testować już jakąś tam bazą danych. Nawet jeśli jest inna. Kosztuje mnie to więcej, ale niewiele więcej, a wynik mam szybciej. Przetestuję poprawność użytych SQL i DML, zmniejszam ryzyko mocksturbacji (overmockingu).

jest trudne, długie i nie dostaje natychmiast wyniku to wolę testować już jakąś tam bazą danych — to tu problemem jest czas działania testów, czyli nic nowego od lat. Przy czym ja nie uważam, że jak przetestuję na bazie in memory, to jestem już bezpieczny (bo wiem, że nie jestem), więc i tak muszę to przetestować na stosie produkcyjnym. Dlatego trudno mi uwierzyć w te opowieści, że da się testować wszystko z prawdziwymi klockami dookoła w 3 sekundy, jak nawet postawienie stosu na cloud formation w AWS zajmuje kilkanaście minut.

Nieliniowo względem czasu pracy włożonego.

Dlatego tutaj trzeba ten czas minimalizować, żeby tworzenie drugiego środowiska na podstawie produkcji było niemal darmowe — infrastructure as a code. A potem tylko podmieniamy connection stringi w konfiguracji i lecimy po klockach chmurowych.

jarekr000000
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: U krasnoludów - pod górą
  • Postów: 4712
1
Afish napisał(a):

Dlatego tutaj trzeba ten czas minimalizować, żeby tworzenie drugiego środowiska na podstawie produkcji było niemal darmowe — infrastructure as a code. A potem tylko podmieniamy connection stringi w konfiguracji i lecimy po klockach chmurowych.

Powiem tylko, że część mojej pracy u klientów to pomoc z wprowadzaniem infrastucture as code (trochę osób nad tym pracuje). Ale na razie jest to daleko, mam tylko kilka systemów i to niekrytycznych przerobionych.
W reszcie problemem jest Oracle, COBOL itp.
W jednej jedynej firmie, która zresztą niestety "wegetuje" widziałem naprawdę dobrze zrobione testy - stawiane kilkadziesiąt mikroserwisów w chmurze - pełna kontrola czasu i symulowanie roku pracy banku (w kilka godzin).
W innych przypadkach - zawsze coś tam jest słabo chmurowalnego i trzeba rzeźbić :-(

Co do testowania na prawdziwym sprzęcie - testujemy też wydruki na prawdziwych maszynach drukujących listy (pocztę do klientów). Greta mnie nie lubi (zwłaszcza jak coś zepsuję :-( ).
Kiedyś okazało się, że w pewnych przypadkach (za wiele wpisów w tabelce czegoś tam) jakaś grafika nachodzi na kod sterujący (do sterowania pakowaniem w koperty) i cała wielka maszyna się zatrzymuje, bo nie wie co robić. Tu był bład w template wyduku, który powinien to rodzielać na osobne strony.
Z tym, że jest to taki przypadek gdzie nadal nie wiem jak taki błąd łapać przed zmarnowaniem drzew. (Ale mam jakieś pomysły - robie symulator tej maszyny)

TS
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 865
1

U mnie testowanie wygląda zazwyczaj tak, że razem z rozwojem projektu rozwijam jednocześnie projekt testów automatycznych do tego projektu. Oczywiście nie mam pokrytego wszystkiego, ale te najważniejsze ścieżki, które dają mi najlepszy obraz dotyczący tego jak wiele funkcji działa.

Co do unit testów to pisze ich bardzo mało bo większość rzeczy pokrywam testami integracyjnymi. Jeżeli widzę funkcję:

Kopiuj
if self.a:
    self.service_a.fun1()
else:
    self.service_b.fun2()

to uważam taką funkcję za trywialną i nie mockuję specjalnie do tej funkcji tych usług bo jeżeli zamockuję wszystko to mój test testuje wyłącznie prosty warunek if else co jest testem implementacji i stratą czasu. Oczywiście zdarzają się trudniejsze przypadki, ale preferuję postawienie całego wyizolowanego stacka testowego dla jednego mikroserwisu i przetestowanie go stukając pod endpointy niż testowanie takich trywialnych funkcji, później ten stack ubijam. Jeżeli moja funkcja wrzuca coś na Kafkę to wolę postawić sobie testową Kafkę i po wrzuceniu czegoś na Kafkę zapuścić funkcję, która mi przeczyta czy to faktycznie zostało wrzucone na Kafkę. Wiem, że przy WebSphere MQ może być to problem, ale w takim przypadku poleciłbym podniesienie w firmie tematu testowej infrastruktury do której można się podpinać podczas testów.

Samo używanie H2 zamiast Oracle uważam za prawie klasyczne podejście z mockowaniem. Bo mockowanie to przecież udawanie usługi, którą się nie jest. H2 zamiast Oracle dodaje tez dodatkową zależność w której trzeba być świadomym różnić między oryginałem, ale framework do mockowania też jest taką zależnością i też trzeba być go świadomym. Jeżeli mój mock udaje jakieś zachowanie to jeżeli zmienię w teście argumenty wywołania to będę musiał zmienić też konfigurację mocka. Najlepszym mockiem byłby oryginał usługi mockowanej. Dlatego przy mockowaniu nie jest o tyle ważny framework co inwencja twórcza. Jeżeli tworzysz crawlera, który skanuje strony html to dobrym mockiem takiej strony będzie po prostu cały plik html, który stawiasz na serwerze typu one-shot.

Ostatnio Lambdy na AWS testowałem w ramach większego scenariusza, mógłbym też dodać do testów warunki podpinania się pod lambdę i sprawdzania czy została wykonana. Sam kod lambdy też powinien zostać otestowany w osobnym środowisku.

Przy testach największą zaletę jaką widzę są:

  • powtarzalność - jeżeli usługa padnie to test musi upaść,
  • szybkość tworzenia kodu - nie muszę ręcznie wyklikiwać danej usługi lub pytać mojego QA o przetestowanie funkcji, mam szybszy feedback,
  • nie boję się modyfikować ani refactorować kodu bo jeżeli coś zepsuję to moje testy to wyłapią.
jarekr000000
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: U krasnoludów - pod górą
  • Postów: 4712
1

Musiałem to wkleić w końcu:

Testy

Mała historyjka:

W jednej firmie pracowicie walczyłem, aby wywalić testy selenium.
Było ich kilkaset, testowały nie tylko GUI, ale w zasadzie całe e2e logikę, wszystko, na prawdziwej infrastrukturze.

Dlaczego walczyłem o wywalenie (i to skutecznie)?

Po kilku miesiącach pracy w nowej firmie (był maj) odkryłem, że:
a) nad utrzymaniem tych testów pracują stale 2 osoby (z 8 miu w zespole)
b) te testy ostatnio były zielone w połowie stycznia (był maj), przed tym styczniem też miesiącami na czerwono
c) stale wywala się kilka testów, jedne naprawione to inne padają, 2 osoby pracują nad tym - ale w najlepszych momentach juz tylko 2 testy są czerwone, następnego dnia 20...
d) w międzyczasie poszło ileś releasów produkcyjnych (swoją drogą - baaardzo ciekawe - to był mój główny argument - po co nam testy jak i tak je olewamy),
e) testy działały na dostępnej "testowej" bazie danych (baz było kilka) i mimo jasnego podziału, których danych i użytkowników nie ruszać ciągle ktoś to ruszał,
f) jak przysiadłem nad tymi testami to stwierdziłem, ze skomplikowana natura systemu (przez security) i dawno zrypany koncept z dzieleniem baz danych z testerami manualnymi powoduje, że jest to nie do odratowania -część scenariuszy tak się rozlazła, że przeczyła sobie !
(gdyby od początku była izolowana baza danych (właściwie kilka baz), ze skryptów, dodatkowo były utrzymywane skrypty konfigurujące inne systemy (security) - to może by się dało)

Team w zasadzie był tak zdemotywowany utrzymaniem tych testów, że wszyscy byli raczej za (choć z wątpliwościami), ja też wątpliwość miałem. Jedynie biznes mocniej oponował (serio - w tej firmie biznes stale prosił o więcej testów, a ja chciałem wywalić te "najlepsze")

Myślałem, o zostawieniu kilku testów, ale w sumie został tylko test logowania (taki smoke).

Po wywaleniu testów wyraźnie było odczuwalne, że pracuje więcej ludzi :-)
Po mniej więcej roku był jeden naprawde przykry dzień, gdzie okazało się, że ważna, ale zapomniana funkcja nie działa na produkcji - bo ją wywalilśmy (happy refactoring). Wtedy jeden człowiek z biznesu przyszedł do mnie i powiedział - "a widzisz - to było sprawdzane w selenium :-)"

Tak czy siak - warto było!

somekind
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
3

@Afish: odpowiadam z opóźnieniem na Twoje pytanie, mimo iż wątek podążył w innym kierunku.

Afish napisał(a):

A jak to rozwiąże problem wtedy też ryzykuję, że pracuję z obiektami niepoprawnymi biznesowo (bo nazwa się nie zgadza czy coś)? Autofixture wypluje mi losowe wartości, więc dostaję coś, co walidacji przejść nie powinno, to koncepcyjnie nie różni się od schowania Company za metodą wstrzykującą mocka walidatora przepuszczającego każdy obiekt. Oczywiście oszczędzam na liniach kodu i gdzie mogę, tam używam Autofixture, ale tutaj nie jestem przekonany.

Problem z przykładu - unikalność Company.Name, AutoFixture zapewnia przecież samo z siebie. A jeśli warunki są bardziej skomplikowane, to AutoFixture można bardzo szeroko konfigurować. Jeśli mam jakieś walidatory, to piszę do nich testy, a w ramach nich swoje ICustomization, które tworzą mi prawidłowe obiekty. Mając już AutoFixture skonfigurowane do tworzenia prawidłowych obiektów, to w którymkolwiek teście bym Company nie użył, to to będzie prawidłowy obiekt i żaden niezwiązany test się fałszywie nie wysypie.

superdurszlak napisał(a):

Tym bardziej, że - przynajmniej takie odnoszę wrażenie - jak już raz stwierdzi się "to jest dobre" i obierze jakiś kierunek, to kod lubi potem powoli, powoli zbiegać sobie do tego ekstremum. Niby każdy wie, niby każdy rozumie, niby każdy się stara, niby coś tam - a prędzej czy później natrafiasz na jakiś miejmy nadzieję niewielki kawałek, gdzie wybiło poza skalę i zastanawiasz się, co poszło nie tak.

No, a potem zamiast przemyśleć sytuację, odkryć, w którym momencie nastąpiło przegięcie, i zastanowić się gdzie jest jakiś rozsądny/optymalny poziom, to stwierdza się, że coś jest "złe, be i harmful" i wpada w odwrotne ekstremum.
Niestety takie uroki w branży modowej, w jakiejś porządnej inżynierii nie byłoby takich odpałów.

Pojawia się też jeszcze jeden temat, którego nie poruszyłem - testowanie aplikacji opartej np. o framework DI, gdzie żonglowanie zależnościami niekoniecznie musi być trywialne, ale jest oddelegowane do frameworka. Wiadomo, że

  • można napisać testy niewymagające stawiania kontekstu frameworka DI, i poskładać wszystko samemu
  • powinniśmy testować aplikację a nie framework, a przecież prawidłowe postawienie kontekstu to w tej sytuacji zadanie frameworka
  • możemy założyć, że skoro testy high-level przechodzą to wszystko jest OK i tak zapewne jest

I tu pojawia się mały problem:

  • szybkie testy bez stawiania kontekstu aplikacji itp. nie wykryją np. zepsutej konfiguracji beanów. Czasem nawet nie tyle zepsutej, co źle napisanej i wrażliwej na zmiany - przez co podbicie wersji frameworka i zmiana jakiegoś wewnętrznego defaulta we frameworku wywala stawianie kontekstu.
  • testy "na bogato" będą pewnie dość wolne i upierdliwe w używaniu na bieżąco. Zresztą nawet jako część pipeline CI byłyby uciążliwe - kto by chciał czekać pół godziny, aż testy na branchu przejdą...
  • można próbować postawić mały kawałek kontekstu (przynajmniej w niektórych frameworkach) zamiast całości i testować taki kawałek, ale to już zakrawa na wcinanie się w implementację. Nie mówiąc o tym, że ww. problemy z zepsutą konfiguracją mogą się objawić dopiero przy stawianiu całego kontekstu, i tak dalej i tak dalej...

@superdurszlak - no, ale gdzie tu problem?
Jeden zestaw testów (jednostkowych) nie korzystają z żadnego frameworka DI, obiekty tworzysz w nich ręcznie i w ten sposób testujesz wyłącznie swój kod.
Drugi zestaw testów (integracyjnych) uruchamia rzeczywistą aplikację i uderza w jej endpointy oczekując pozytywnych wyników, wtedy wiesz, że fasolki zagrały prawidłowo.

jarekr000000 napisał(a):
  1. Testy to testy - żadnych głupich podziałów na unitowe, komponentowe, integracyjne itp.

Wszyscy równi, każdemu według potrzeb... Były takie idee już w historii, gdzieniegdzie eksperymenty nadal trwają i zdaniem ich autorów są pasmem nieprzerwanych sukcesów.
Ale nie wiem, czy się sprawdzą tutaj, bo jedne testy są szybkie, więc chcemy je często uruchamiać, inne są wolniejsze, więc uruchamiamy je tylko co jakiś czas, np. pod koniec pracy nad zadaniem, a jeszcze innych nie da się lokalnie uruchomić.

  1. Żadnych mockitów i innych cudów - testujemy nasz kod a nie mockito. Jak jest coś w stylu mocka potrzebne, to piszemy to ręcznie - paradoks z kilku projektów jest taki, że dzięki temu mamy mniej kodu...

No jeśli nie da się mocka wygenerować w locie to być może faktycznie lepiej go napisać samemu. Ale szczerze wątpię, biblioteka mockująca musiałaby mieć bardzo rozwlekłe API, żeby jej używanie wymagało więcej kodu.

Używanie biblioteki mockującej nie sprawia automatycznie, że testy ją testują. To kwestia designu - zarówno kodu produkcyjnego jak i testów.

  1. Oczywiście żadnych spy i testowania czy metoda była wywołana, jeśli testowany kod potrafi zwrócić oczekiwane wyniki bez wyciągania czegokolwiek z repo. innych serwisów itp. - to tym lepiej dla kodu.

Piękne założenie, tylko co jeśli testowany kod nie zwraca wyników, bo jego celem jest efekt uboczny? Jeśli nie napiszę testu sprawdzającego, czy metoda mocka została wywołana, to mogę coś zepsuć np. refaktoryzacją, a dowiem się o tym za późno, bo dopiero podczas testów na jakimś realnym środowisku, oby nie na produkcji.

superdurszlak
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Kraków
  • Postów: 2002
1

Nie zdążyłem jeszcze podziękować wszystkim za odpowiedzi, także robię to teraz ;)

Szczególnie @Afish i @jarekr000000 - w waszej dyskusji było sporo ciekawych argumentów z różnych punktów widzenia. Gdybym mógł, to zaakceptowałbym odpowiedzi jednego i drugiego, no ale da się tylko jedną - a patrząc z boku na to wszystko wniosek jest dość smutny - jak się nie obrócić, d**a będzie z tyłu, a jak do równania dorzucić legacy (legacy-legacy, nie kod z poprzedniego sprintu :] ) i inne problemy, to kolejne wyrastają jak grzyby po deszczu w nietypowych miejscach...

somekind napisał(a):

w jakiejś porządnej inżynierii nie byłoby takich odpałów.

¯\_(ツ)_/¯
No wiesz, teoretycznie mamy te wszystkie dobre praktyki - w każdym razie z nazwy, jakieś wytyczne co robić a czego ani się waż, wszyscy potrafimy sobie wyprodukować jakieś metryki i cyferki, które to mają nam powiedzieć po inżynierskiemu czy wszystko się zgadza. Monitoringi, logi, cyferki, kolorki, jak zielono to dobrze, jak czerwono to niedobrze... Ale tak, w praktyce pod wieloma względami inżynierii oprogramowania nadal bliżej do rzemiosła oprogramowania, sztuki nowoczesnej oprogramowania albo trwałej prowizorki oprogramowania niż jakiejkolwiek inżynierii.

@superdurszlak - no, ale gdzie tu problem?
Jeden zestaw testów (jednostkowych) nie korzystają z żadnego frameworka DI, obiekty tworzysz w nich ręcznie i w ten sposób testujesz wyłącznie swój kod.
Drugi zestaw testów (integracyjnych) uruchamia rzeczywistą aplikację i uderza w jej endpointy oczekując pozytywnych wyników, wtedy wiesz, że fasolki zagrały prawidłowo.

I z grubsza takie coś właśnie najczęściej widzę. Ale dla mnie problem w tym podejściu jednak jakiś jest:

  • pierwszy zestaw nie testuje wszystkiego - jeżeli np. kodując lokalnie odpalam tylko te testy, to nie mogę nim sprawdzić wszystkiego i np. to, że rozstroiłem fasolki odkryję dopiero później, jak już odpalę drugi zestaw
  • drugi zestaw testów stawia wszystko itd, przez co może być względnie czasochłonny i/lub setup środowiska jest złożony, może wymagać specyficznego setupu, może być zbyt zasobożerny by postawić go lokalnie... i tak dalej. To samo może dotyczyć innych, pochodnych testów które wymagają postawienia całości, np. performance.
  • Przez wzgląd na powyższe zaczyna się szukać kompromisów, na bieżąco odpalać UT a IT/e2e już rzadziej, np. po wypchnięciu brancha jako część jakiegoś pipeline. Ale z tego powstają potem rytuały - a rytuały są zaprzeczeniem inżynierii. Chyba, że nazwiemy je procesami, a wtedy mamy rozbuchane procesy za którymi podobno też nie przepadamy, szczególnie gdy wymyśla je kto inny. Mogę stwierdzić, że wcisnę swój proces w pipeline i problem znika - ale pipeline też trzeba utrzymywać i rozwijać, jak wrzucę do niego cały śmietnik nie będzie dobrze.
  • Przez procesy, rytuały i dzielenie testów na zestawy odpalane na różnych etapach wydłuża mi się feedback loop - nie dowiem się od razu, czy moja zmiana psuje fasolki albo performance, bo to już broszka jakiejś wyższej instancji.

Ja nie mówię, żeby od razu rzucać wszystko, mieć jeden zestaw ze wszystkim i żmudnie odpalać wszystko po każdej zmienionej linijce albo trzech. Ale to jest jedna z tych sytuacji, gdy rozwiązując jeden problem tworzymy kolejne i wystawiamy się na strzał z innej strony.

Jak się uprzeć, to może nawet dałoby się sformułować sobie jakiś tam analog CAP albo raczej PACELC theorem dla testów. Taki, co by w mądrych, ładnych słowach grzmiał o tym, że nie można mieć wszystkiego i przy rosnącej "złożoności" testowanego systemu (środowisko, wielkość, zależności, startup time, ogon kompatybilności wstecznej, liczba nodów jeśli jest rozproszony etc) trzeba wybierać między "dostępnością" testów (odpalam je kiedy chcę i mam szybki feedback) a ich "kompletnością"/"wiernością" (testuję na systemie jak najbliższym prawdziwemu i w warunkach jak najbliższym produkcyjnym). Może ktoś to już nawet oficjalnie zrobił i tego nie wiem :D

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.