07 gru 2022 POIT #180: Dobre praktyki w testowaniu na przykładzie .NET
Witam w sto osiemdziesiątym odcinku podcastu „Porozmawiajmy o IT”. Tematem dzisiejszej rozmowy są dobre praktyki w testowaniu na przykładzie .NET.
Dziś moim gościem jest Dominik Jeske – związany z mBankiem od 3 lat. W tym czacie wraz z zespołem architektury pracował nad standaryzacją architektury używanej przez deweloperów oraz nad wypracowaniem procesów CI/CD pozwalających wdrażać oprogramowanie wysokiej jakości w sposób niezależny. Ponadto dzieli się wiedzą przez wszelakie media jak blog czy kanały audio/video. Zawodowo programowaniem zajmuje się od 16 lat z czego większość poświęcił technologii .NET. Jest administratorem jednej z największych grup społeczności .NET na Facebook’u.
Sponsor odcinka
Sponsorem odcinka jest mBank S. A.
W tym odcinku o dobrych praktykach w testowaniu rozmawiamy w następujących kontekstach:
- czy testowanie jest już powszechne?
- czemu nie chcemy pisać testów?
- jakie są zalety testowania?
- czy możemy być czasem uśpieni zbytnio wygórowanym przekonaniem, że testy nas zawsze uratują?
- co to znaczy, że test jest dobry?
- jak dobrze wykorzystywać mocki w testowaniu?
- jakie są code smells albo złe praktyki w testowaniu?
- jakie są przydatne biblioteki w .NET ułatwiające testowanie?
Subskrypcja podcastu:
- zasubskrybuj w Apple Podcasts, Google Podcasts, Spreaker, Sticher, Spotify, przez RSS, lub Twoją ulubioną aplikację do podcastów na smartphonie (wyszukaj frazę „Porozmawiajmy o IT”)
- ściągnij odcinek w mp3
- poproszę Cię też o polubienie fanpage na Facebooku
Linki:
- Profil Dominika na LinkedIn – https://www.linkedin.com/in/dominik-jeske-46757aa2/
- Blog Dominika – https://dominikjeske.github.io/art-of-unittests-part1/
- https://www.itwmbanku.pl/
Wsparcie na Patronite:
Wierzę, że dobro wraca i że zawsze znajdą się osoby w bliższym lub dalszym gronie, którym przydaje się to co robię i które zechcą mnie wesprzeć w misji poszerzania horyzontów ludzi z branży IT.
Patronite to tak platforma, na której możesz wspierać twórców internetowych w ich działalności. Mnie możesz wesprzeć kwotą już od 5 zł miesięcznie. Chciałbym oddelegować kilka rzeczy, które wykonuję przy każdym podcaście a zaoszczędzony czas wykorzystać na przygotowanie jeszcze lepszych treści dla Ciebie. Sam jestem patronem kilku twórców internetowych i widzę, że taka pomoc daje dużą satysfakcję obu stronom.
👉Mój profil znajdziesz pod adresem: patronite.pl/porozmawiajmyoit
Pozostańmy w kontakcie:
- 📧 Jeśli masz jakieś pytania lub komentarze, pisz do mnie śmiało na krzysztof@porozmawiajmyoit.pl
- 📩 Zapisz się na newsletter, aby nie przegapić kolejnych ciekawych odcinków
- 🎙 Subskrybuj podcast w lub
Muzyka użyta w podcaście: „Endless Inspiration” Alex Stoner (posłuchaj)
Transkrypcja podcastu
To jest 180. odcinek podcastu Porozmawiajmy o IT, w którym z moimi gośćmi rozmawiam o dobrych praktykach w testowaniu na przykładzie .NET. Sponsorem tego odcinka jest mBank S.A. Przypominam, że w poprzednim odcinku rozmawiałem o ścieżkach kariery w testingu oprogramowania. Wszystkie linki oraz transkrypcję dzisiejszej rozmowy znajdziesz pod adresem porozmawiajmyoit.pl/180.
Ocena lub recenzja podcastu w Twojej aplikacji jest bardzo cenna, więc nie zapomnij poświęcić na to kilku minut. Od niedawna można wystawiać oceny podcastom w Spotify. Będzie mi bardzo miło, jeśli w ten sposób odwdzięczysz się za treści, które dla Ciebie tworzę. Dziękuję.
Ja się nazywam Krzysztof Kempiński, a moją misją jest poszerzanie horyzontów ludzi z branży IT. Środkiem do tego jest między innymi ten podcast. Wspierając mnie przez Patronite, dzieląc się tym odcinkiem w swoich kręgach lub feedbackiem na jego temat ze mną, pomagasz w realizacji tej misji. Ja natomiast bardzo dziękuję moim obecnym patronom. A teraz życzę Ci już miłego słuchania!
Odpalamy!
Cześć!
Mój dzisiejszy gość jest związany z mBankiem od trzech lat. W tym czasie wraz z zespołem architektury pracował nad standaryzacją architektury używanej przez deweloperów oraz nad wypracowaniem procesów CI/CD, pozwalających wdrażać oprogramowanie wysokiej jakości w sposób niezależny. Ponadto dzieli się wiedzą poprzez wszelakie media jak blog czy kanały audio-wideo. Zawodowo programowaniem zajmuje się od 16 lat, z czego większość poświęcił technologii .NET. Jest administratorem jednej z największych grup społeczności .NET na Facebooku. Moim i Waszym gościem jest Dominik Jeske.
Cześć, Dominiku! Bardzo mi miło gościć Cię w podcaście.
Cześć!
Dzisiaj z Dominikiem będziemy rozmawiać o dobrych praktykach w testowaniu oprogramowania. Głównie skupimy się na perspektywie Software Dewelopera, będziemy się w tym posiłkować przykładami z .NET-u, czyli technologii, w której Dominik się specjalizuje, ale myślę, że ta wiedza, którą Dominik się z nami podzieli, będzie na tyle uniwersalna, że spokojnie można ją zastosować również do większości innych technologii.
Ale zanim do tego przejdziemy, to chciałbym Cię, Dominik, zapytać na wstępie, czy słuchasz podcastów, jeśli tak, to może masz jakieś fajne, o których warto tutaj wspomnieć?
Tak, czasami zdarza mi się słuchać podcastów, chociaż bardziej słucham audiobooków, ale jeżeli chodzi o takie techniczne rzeczy, to czasem zdarza mi się posłuchać podcastów. Ostatnio słucham np. Better Software Design, Mariusza Gila i czasami też .NET Rocks! bardziej związany z .NET-em. A ten pierwszy jest bardziej architektoniczny.
Super, to na pewno warte polecenia podcasty. Na początku chciałbym Cię zapytać o taką, powiedziałbym, świadomość w branży, jeśli chodzi o testowanie. I tak jak mówiłem na początku: z perspektywy Software Dewelopera. Czy wg Ciebie to testowanie jest już czymś oczywistym stosowanym przez inżynierów software developmentu?
Myślę sobie, że skoro cały czas pytamy na rozmowach rekrutacyjnych o te umiejętności, to może jest to czymś tak oczywistym, ale jestem bardzo ciekawy Twojego zdania, Twojej perspektywy na tę powszechność testowania wśród programistów.
Na pewno powszechne jest przekonanie, że musimy testować. Dużo się o tym mówi, jest to w teorii taki standard, ale ja bym to troszkę porównał do takiego powiedzenia, że musimy myć ręce. I każdy niby myje te ręce, ale jak była pandemia, to się okazało, że zaczęliśmy się dopiero uczyć, jak dobrze to robić. Więc kwestia nie tego, czy to robimy, ale jak to robimy. Czy te testy są robione dobrze. Możemy sobie odhaczyć w Excellu, jak to robią menedżerowie, że tak, robimy testy, więc jest okej. W meritum tego jest tak naprawdę, jak my robimy te testy i myślę, że sobie jeszcze o tym porozmawiamy.
Wobec tego warto byłoby ten temat pociągnąć w tym kierunku testo sceptycyzmu, bo ciągle spotyka się programistów, którzy twierdzą, że nie do końca jest w tym wartość, że to może strata czasu. Właśnie. Czemu nie chcemy pisać testów? Nie umiemy? Nie widzimy w tym wartości?
Jak sobie analizowałem ten temat, wypisałem takich siedem grzechów, które popełniamy w branży, przynajmniej te, które mi przyszły do głowy. Przede wszystkim najważniejsza dla mnie sprawa jest psychologiczna, od kogo wychodzi ta potrzeba pisania testów, czy to ktoś chce od nas i troszkę to na nas wymusza, czy to jest tak, że my jako inżynierowie chcemy coś tam dostarczyć w dobrej jakości i sami chcemy te testy pisać.
Więc czasami niestety jest to ta pierwsza opcja, a więc przychodzi do nas ktoś z góry i nam każe pisać te testy, i my nigdy do tego wtedy dobrze nie podchodzimy, zawsze jest tak, że jest jakiś psychiczny opór, wykonujemy nie do końca tak, jak być powinno itd. Więc coś tam napiszemy, jakiś test, ale nie wychodzi to z wewnętrznej potrzeby, więc to jest dla mnie taki główny czynnik.
Innym takim czynnikiem jest prozaiczna sprawa, że po prostu nie potrafimy dobrze pisać tych testów i dlatego trochę tego unikamy. Mówi się, jak napisać test technicznie, ale mało mówi się o tym, jak dobrze to robić, żeby były utrzymywalne, żeby nie skończyło się tak, że wyłączamy, bo nie panujemy nad nimi. Generalnie, żeby te testy miały wartość, a nie tylko były napisane. Więc jednak taka wiedza o tym, jak to dobrze robić, jest też istotna. Oczywiście to przychodzi trochę z czasem, ale warto ją szlifować. Ja to nazywam sztuką pisania testów, bo to jednak trzeba troszkę doświadczenia, żeby zrobić to dobrze.
Kolejnym takim czynnikiem jest złe środowisko. Jeżeli mamy nawet intencję, że chcemy napisać te testy, ale dookoła wszyscy mówią, że nie, nie piszmy tego, albo olewają nasze testy, my je robimy, nikt na to nie zwraca uwagi, to po jakimś czasie, jak to się mówi, z kim przystajesz, taki się stajesz, czyli troszkę to środowisko na nas wpływa. Więc to jest też istotne, żeby dbać o to środowisko pracy tak, żeby jednak to testowanie nie było takie drugiej kategorii, traktowane jako niepotrzebne. Dla mnie to powinien być jeden z nieodłącznych czynników procesu tworzenia oprogramowania. Nie coś dodatkowego, tylko coś, z czym się nie dyskutuje.
To się też wiąże z kolejnym punktem, który nazwałbym w skrócie: „biznes nie chce”. Czyli spotkałem się z takimi wewnętrznymi tłumaczeniami, dlaczego tego nie robimy, to zawsze coś wymyślimy, jakiś dobry argument. I się spotkałem z takim czymś, że no, bo my rozmawialiśmy z biznesem, przedstawiliśmy im, że zajmie to tyle i tyle, ale tu jeszcze trzeba zrobić testowanie, i biznes stwierdził: nie, nie, to już nie testujcie. Więc tak naprawdę nie powinniśmy przedstawiać tego jako opcji biznesowi, bo biznes zawsze utnie, on się nie zna na wytwarzaniu oprogramowania. To powinno być po prostu wbudowane w ten nasz proces i od razu wliczone, nie powinno być jako opcja, bo wtedy czasami tak właśnie wychodzi.
Następnym czynnikiem jest coś takiego na zasadzie „zły dotyk boli całe życie”, czyli ktoś się zraził, coś się źle wydarzyło z testami, coś nie poszło, to może jednak lepiej nie, po co tracić czas na te testy, mieliśmy testy w poprzednim projekcie i one nam nie działały. Mają generalnie złe doświadczenie.
Przedostatnia rzecz to niepatrzenie na długoterminowość tego, co nam dają testy. Ja to zawsze porównuję do remontowania dróg. Jak się patrzy krótkoterminowo, to jest tak, jak bywało w naszym kraju, od lat 90. przyjeżdżają panowie, zalewają dziurki i robią to co roku od nowa. Podobnie jest z testowaniem. Jeżeli tego się nie zrobi, to będziemy potem robili jakieś łatki, szpachelki itd., i ten tworem będzie nam rósł. A jak się do tego dobrze zabierzemy do testów, to jednak one nam trochę trzymają w ryzach to oprogramowanie. Długoterminowo one procentują, ale nie zawsze to się widzi od razu.
I ostatnich, siódmym grzechem jest lenistwo. Nie chce nam się tego robić, mamy różne wymówki. Programista ma dowieźć funkcjonalność dla biznesu. Mam zrobić kalkulator – zrobiłem 2 + 2 = 4, wynik wyszedł i zrobione. Więc to jest dla mnie tych siedem grzechów.
To się też wiąże z kolejnym punktem, który nazwałbym w skrócie: „biznes nie chce”. Czyli spotkałem się z takimi wewnętrznymi tłumaczeniami, dlaczego tego nie robimy, to zawsze coś wymyślimy, jakiś dobry argument. I się spotkałem z takim czymś, że no, bo my rozmawialiśmy z biznesem, przedstawiliśmy im, że zajmie to tyle i tyle, ale tu jeszcze trzeba zrobić testowanie, i biznes stwierdził: nie, nie, to już nie testujcie. Więc tak naprawdę nie powinniśmy przedstawiać tego jako opcji biznesowi, bo biznes zawsze utnie, on się nie zna na wytwarzaniu oprogramowania. To powinno być po prostu wbudowane w ten nasz proces i od razu wliczone, nie powinno być jako opcja, bo wtedy czasami tak właśnie wychodzi.
Gdy wymieniałeś te grzechy, zrobiłem sobie szybki rachunek sumienia, wyszło mi, że przynajmniej kilka z nich popełniłem gdzieś tam w różnych projektach.
Tak, ja też.
Myślę, że jak każdy, prawda?
Tak, to nie jest tak, że ja jestem tutaj jakimś ideałem. Ja tak naprawdę przez to, że nie byłem dobrze wychowany w testach, to trochę to do mnie wraca i muszę się sam biczować troszkę w zaciszu swojego pokoju, żeby nie mieć tych wymówek.
A gdybyś trafił do takiego projektu, firmy, zespołu, który, powiedzmy, nie do końca wierzy w testy, nie do końca widzi ich wartość, to w jaki sposób byś tę ideę testowania oprogramowania sprzedał? O jakich zaletach byś powiedział?
Na pewno jednym z takich głównych czynników, który doceniłem dopiero po latach, jest to, że lubię mieć spokojny sen. Czyli jest jakaś sytuacja, zmieniamy coś w oprogramowaniu i czy ja chcę mieć telefon w nocy? Więc jednak jeżeli sobie dobrze te testy przygotujemy, to one nam wyłapią, może nie na 100%, ale na pewno zmienimy coś w kluczowym miejscu i mamy ten spokojny sen, przeświadczenie, że mamy to jakoś sprawdzone. To nie jest tak, że mamy jeden, wielki domek z kart, wyciągnęliśmy jedną kartę i z tyłu głowy mamy, że w którymś momencie to się może przewrócić. Więc ten spokojny sen się docenia dopiero później. Bo jak się jest młodszym, to się uważa, że jak się zmieni za jakiś czas pracę, to już nie będzie trzeba przy tym siedzieć, ale to jest naprawdę bardzo istotna sprawa, jeżeli chodzi o tę kwestię. I w podobnym kontekście można powiedzieć refaktoryzacji, że nie tylko my musimy tę refaktoryzację robić.
Załóżmy, że mówimy tu o perspektywie kierownika projektu, kogoś, kto nie jest programistą, który przychodzi, odchodzi, ale on będzie ten produkt utrzymywał. I teraz, programista sobie wytworzył jakiś kod, poszedł sobie, i teraz nagle biznes przychodzi do tego kierownika i mówi mu: tutaj musimy zrobić taką poważną zmianę. I teraz wszyscy się zaczynają bać po kątach, bo nikt nie zna tego kodu, to jest jakaś newralgiczna część systemu i każdy boi się tego dotknąć, bo nie wiadomo, co wybuchnie. Więc to jest bardzo istotna sprawa, żeby robić te testy i mieć takie poczucie bezpieczeństwa.
Również istotne jest to, że jeżeli zaczynamy testować, to jak to mówi mój obecny szef, zakładamy czapkę testera i mamy troszkę inny mindset. Tak jak powiedziałem, że programista ma swój cel, żeby wytworzyć tego taska i go zamknąć, to on nie do końca nastawia się na tzw. warunki brzegowe, nie zadaje sobie pytań a co, jeśli to czy tamto. To jest trochę inna perspektywa. Jak zaczynamy testować, to zaczynamy myśleć w trochę inny sposób o naszym oprogramowaniu: a co, jeśli tutaj będzie null, a co, jeśli coś złego się tu wydarzy? Myślimy poza tzw. Happy Puffem. Więc tutaj trochę bardziej się rozwijamy pod kątem takiego myślenia i też architektonicznie.
Bo jak ktoś pisał testy i pisał ich trochę więcej, to zauważ, że testy wymuszają na nas lepszą architekturę. Jeżeli musimy coś przetestować, to nagle się okazuje, że o kurczę, tutaj mamy za dużo zależności, jak ja mam to w ogóle przetestować, skąd ja mam to wziąć, dlaczego ja w ogóle tego potrzebuję – zaczynam się zastanawiać, inaczej to układać. I wychodzi nam na to, że tak naprawdę testy nie są tylko częścią tego quality takiego końcowego, czy są błędy, czy ich nie ma, ale też polepsza nam się ta architektura, i to jest taki trochę ukryty bonus tego testowania.
Czyli nie tylko same testy, ale też architektura się polepsza, a dodatkowo jeszcze dokumentacja. Bo nikt nie lubi pisać dokumentacji, zawsze zostawia się to na koniec i później tego nie ma, bo tak jest, że jak coś się zostawia na koniec, to tego się nie robi, a testy są idealnym motywem do dokumentacji. Mamy dobre testy, to wystarczy spojrzeć, przejrzeć warunki testowe i mamy już jakieś pojęcie, jak to działa. Oczywiście, jeżeli one są dobrze przygotowane.
I jeszcze jeden aspekt, błąd, który popełniałem na początku mojej przygody z oprogramowaniem, jak robiłem sobie jednorazówki, gdy testowałem program, coś uruchamiałem, żeby sprawdzić, czy się uruchomi, i czasami tę pracę wykonywałem X razy, bo nieraz coś tam wiązałem sobie na sznurki, potem musiałem sobie jeszcze raz te sznurki powiązać. I tak naprawdę te testy dają też po jakimś czasie, jak się zrobi automatyzację tych rzeczy, które robimy, takie szybkie deweloperskie testowanie. Czyli zamiast stawiać sobie całe to środowisko, co czasami trwa i też inny programista musi to zrobić, trzeba mu wytłumaczyć, jak to postawić itd., więc nawet to deweloperskie testowanie może nam przyspieszyć.
Więc jest wiele aspektów, które nie zawsze są oczywiste. Nie tylko same błędy i spokojny sen, ale też architektura, dokumentacja, szybsza deweloperka. Tylko trzeba zainwestować w ten pierwszy pułap. Jak zawsze w życiu. Musimy zainwestować, żeby mieć procenty.
No tak. Ta większa pewność, o której powiedziałeś, ten spokojny sen, kiedy wdrażamy coś na produkcję, kiedy robimy refaktoryzację i jesteśmy przynajmniej w jakimś tam stopniu dużo większym, niż gdyby tych testów nie było, pewni, że nic krytycznego nam się nie wysypie, to jest to coś bardzo ważnego.
Zmierzam do tego, żeby powiedzieć, że możemy polegać na tych testach, że to jest w jakiś sposób zabezpieczenie tego, co robimy. Ale z drugiej strony możemy też pójść z tym za daleko, bo jak mówi stara myśl: co się mierzy, to rośnie. I w przypadku testów mamy chociażby coś takiego, jak pokrycie testów, Test Coverage, kiedy zależy od podejścia, od filozofii. Albo dążymy do 100-proc. pokrycia, albo mamy nieco luźniejsze podejście. I w momencie, gdy zaczynamy za bardzo ufać testom, kiedy pokładamy 100-proc. przekonanie w tym, że testy nas zawsze uratują, to może to też trochę działać na naszą niekorzyść. Jestem ciekawy, jak do tego podchodzisz.
To jest bardzo ciekawa kwestia. Na pewno warto wspomnieć, że jeżeli mamy jakiś cel, to musi on być mierzalny. Jeżeli docieramy do celu, to musimy go zmierzyć. W wielu książkach motywacyjnych itp. można spotkać takie stwierdzenie, że najpierw trzeba wybrać sobie cel, ale on musi być mierzalny, żeby zmierzyć go wcześniej, przed i po. Tylko bardzo istotna podstawa to jaka jest ta miara. Jedną z najbardziej popularnych miar jest właśnie to pokrycie kodu, o którym wspomniałeś. To jest nie do końca dobra miara, tzn. tak naprawdę to jest dobra i niedobra miara. I o tym wspomniał jeden z moich ulubionych autorów, który najbardziej do mnie trafia, jeżeli chodzi o oprogramowanie, Vladimir Khorikov. On napisał taką książkę Unit Testing Principles Practices and Patterns. I w tej książce zawarł takie stwierdzenie: code metrics are good bad indicatores but bad positive ones. I chodzi o to, że metryki powiedzą nam, że mamy mało testów, i to jest okej, to jest taka metryka, którą możemy sobie zawsze ustawić, że jeżeli mamy za mało testów, to jednak tutaj coś jest nie tak, bo 0, 20 czy 30%, zależy jak do tego podejdziemy, tutaj na pewno nam coś ta metryka daje, ale jednak z tym 100-proc. pokryciem to jest takie trochę złudne.
Czyli po pierwsze robimy tylko Google testy, żeby te 100% było, czyli wymyślamy. Bo tak naprawdę czym jest to pokrycie kodu? To jest po prostu taki, nazwijmy to, głupi algorytm, bo on nie patrzy, co testujemy, tylko czy program przechodzi przez daną część naszego kodu, i jeżeli wszystkie te możliwe ścieżki przejścia są pokryte testem, to okej, to mamy 100% pokrycia. Ale część z tego jest nam w ogóle niepotrzebna, bo to jakaś część, która nie daje nam nic wartości biznesowej, a część jest taka, że co z tego, że pokryjemy jakimś kodem, nazwijmy to naiwnym testem, skoro ten sam kod może mieć jeszcze 10 innych warunków testowych, których nie użyliśmy, a sama metryka on tym nie powie. Bo ona mówi tylko, czy tutaj gdzieś był użyty ten kod jakoś.
Więc moglibyśmy napisać 100% pokrycia kodu naiwnym testem, który nic nie testuje i który można wyrzucić do kosza, bo tylko musimy go utrzymywać. Więc to jest bardzo krucha metryka, ale na pewno istotna. Tylko że właśnie pod tym względem, co powiedziałem, czyli że jeżeli jest jakieś minimum do utrzymania, chcemy mieć jakąś tam minimalną jakość, wtedy możemy tej metryki użyć, ale nie ufałbym temu aż tak bardzo, że jeżeli metryka mi pokazuje 80% i wszędzie się pali na zielono, to jest wszystko okej. Bardziej bym patrzył na to, jak te testy są napisane.
Bo tak naprawdę czym jest to pokrycie kodu? To jest po prostu taki, nazwijmy to, głupi algorytm, bo on nie patrzy, co testujemy, tylko czy program przechodzi przez daną część naszego kodu, i jeżeli wszystkie te możliwe ścieżki przejścia są pokryte testem, to okej, to mamy 100% pokrycia. Ale część z tego jest nam w ogóle niepotrzebna, bo to jakaś część, która nie daje nam nic wartości biznesowej, a część jest taka, że co z tego, że pokryjemy jakimś kodem, nazwijmy to naiwnym testem, skoro ten sam kod może mieć jeszcze 10 innych warunków testowych, których nie użyliśmy, a sama metryka on tym nie powie. Bo ona mówi tylko, czy tutaj gdzieś był użyty ten kod jakoś.
Czy wobec tego myślisz, że w ogóle warto mierzyć, w ogóle patrzyć na różnego typu metryki związane z testami, czy to nas może po prostu zwieść na manowce?
Jakieś metryki zawsze musimy mieć. Na końcu to się sprowadza do takiego jakiegoś wskaźnika, który coś nam ma powiedzieć. Więc na PR-ach możemy się starać jakoś o tym dyskutować, ale na końcu powinien być jakiś gateway czy coś takiego, co nam gdzieś przepuści kod czy nie przepuści, więc jakąś metrykę powinno się zastosować, ale jednak gdzieś ten człowiek musi myśleć, czyli jednak nie zawierzamy do końca automatom. GPS-owi też nie powinniśmy ślepo zawierzać, bo możemy wjechać do jeziora. Więc tutaj jednak trochę z każdego, jakaś metryka, ale też zdrowy rozsądek i praktyka.
Głównym tematem naszej rozmowy jest dobre testowanie, tak bym to określił. Gdy mówiłeś o głównych grzechach, dlaczego nie testujemy, wspomniałeś, że często nie wiemy, jak to robić, że nawet nie pod kątem technikaliów, konkretnych bibliotek, rozwiązań, ale po prostu nie wiemy, jak sformułować, jak skonstruować dobry test. Jak powinien wyglądać i czym w ogóle jest dobry test? Czego unikać, do czego dążyć?
Na ten temat można by napisać książkę, i to jest ta książka, o której wspomniałem, Vladimira Khirkova , ona bardziej szczegółowo to opisuje, ja się skupię teraz na kluczowych sprawach, metrykach, ale bardziej ogólnych.
Pierwsza sprawa to, że przede wszystkim test powinien być stabilny. Jest to zmora większych projektów, że czasami jakiś test po prostu przestaje działać. Nie działa jednorazowo, nie działa, bo ktoś zrobił jakąś zmianę w zupełnie innym miejscu systemu. Jest to istotne, żeby te testy były solidne i żebyśmy widzieli, co się w nich dzieje. Nie powinno być tak, że nagle nam coś przestaje działać i musimy ponawiać test trzy razy, żeby nam przeszło. Wtedy już powinniśmy wiedzieć, że coś się źle dzieje.
Więc przede wszystkim nasze testy muszą być dobrze odizolowane. Nie możemy w naszych testach zależeć od czegoś, co nie jest nasze. Nie możemy sobie puścić testu, w którym korzystamy z bazy danych, nad którą nie mamy kontroli, i nagle administrator tej bazy danych robi sobie maintenance albo restart czy coś takiego. To jest niedopuszczalne, całość musi być pod naszą kontrolą, więc ta izolacja jest istotna.
Druga sprawa to jest testowanie tzw. internali, czyli jeśli trochę nadgorliwie pójdziemy i zaczniemy testować, to nagle się okaże, że te nasze testy są trudne w utrzymaniu, bo są niestabilne, bo cokolwiek chcemy zmienić, to te testy nam się wysypują. A tak naprawdę to nie do końca o to chodzi. Jeśli spojrzymy sobie tak bardziej holistycznie na te testy i spojrzymy np. na samochód, to jeżeli testujemy kierownicę, to chodzi tylko o ten interfejs, bo chodzi o to API, z którym ktoś się styka, czyli ktoś dotyka tej kierownicy i nią rusza sobie, możemy sobie zrobić test, co się wydarzy, a tak naprawdę nie interesuje nas, jak to jest wykonane pod spodem.
A jeżeli będziemy testowali, jak to jest wykonane, to troszkę sami sobie robimy problemy. Bo tak naprawdę co za różnica, jak to jest wykonane. Jeżeli wykonuje jakąś funkcję, to wykonuje jakąś funkcję. Jeżeli zejdziemy sobie za nisko, to będziemy mieli taką sytuację, że ta stabilność tych testów jest mała.
Możemy też się uzależnić od takich niestabilnych kawałków kodu, jeżeli chodzi o oprogramowanie, np. czasu. W .NET jest takie coś, jak Data.now, w każdym języku można pobrać bieżący czas. I jeżeli się uzależnimy od takiego czegoś, to jest też taka tykająca bomba. Bo możemy sobie zrobić test, który uruchomi się przed północą, skończy po północy, i różne takie tricki, dni przestępne czy cokolwiek innego sobie wymyślimy, i nagle się okaże, że bach! – nasz test nie działa. Więc nad czasem też musimy panować, więc żadne Data.now.
Problemem jest też brak Single Responsibility, więc musimy się tutaj skupić na tym, żeby nas kod robił jedną rzecz. Bo jeżeli nagle się okaże, że nasz kod robi wiele rzeczy, to jest właśnie to, co wspomniałem wcześniej, że gdzieś tam ktoś coś zmieni, a tu nagle nasze testy wybuchają. Więc jeżeli się skupimy, że nasze testy mają jedną odpowiedzialność, to nie tylko jest dobra praktyka z punktu widzenia inżynierii programowania, często się mówi o tym Single Responsibility, ale też właśnie w testach jest to istotne, że wiemy, że jeżeli zmienimy biznes w miejscu A, to testy z tego miejsca A się mogą wysypać, ale tylko tutaj, a nie gdzieś tam u kolegi w innym zespole.
Powinniśmy również się skupiać nad izolacją, jeżeli chodzi o biznes i infrastrukturę. Jeżeli mamy kody testowane biznesowych rzeczy, to to, że zmienimy sobie HTTP, requesty na gRPC albo na WCF-a czy na inne jakieś technologie, to nie powinno wpłynąć na nasze testy biznesowe. Więc tutaj też ta architektura powinna być wyczyszczona. Infrastruktura swoją drogą, biznes – swoją.
Również w tej materii zbyt duża liczba zależności, czyli trochę podobna sytuacja jak z Single Responsibility. Jeśli widzimy w teście, że dana klasa ma 20 zależności, to wtedy już powinno się żółte światełko zaświecić. Rozdzielmy to na osobne części i wtedy może ten test będzie stabilniejszy. Łatwiej się utrzymuje coś, co się zmienia rzadziej niż taki zlepek różnych rzeczy, które będą nam wybuchały za każdym razem, jak coś zmieniamy.
I jeszcze kwestia tego, że zbyt dużo czasami testujemy w jednym teście. Jeżeli zbyt dużo testujemy, to wtedy on wybucha. I czasami wybucha na pierwszym asercie, raz na dziesiątym, wiec tych assertów też nie powinno być zbyt dużo. Lepiej byłoby podzielić na testy, które sprawdzają poszczególne aspekty, i wtedy wiemy, że jak nam się zapali lampka od konkretnego scenariusza testowego, to nie jest lampka od całej naszej funkcjonalności, tylko od jakiegoś kawałka i łatwiej się tego szuka.
I w tej kwestii stabilności jeszcze tzw. side efekty. Czyli żebyśmy starali się pisać kod bardziej funkcyjny. Bo kod funkcyjny to jest coś takiego, co matematycznie przyjmuje input, zwraca output i to jest to, czego się spodziewamy. Wszystkie inne tzw. side efekty, jak np. zapisanie do pliku, coś gdzieś z boku jakiś zapis, to nagle się okazuje, że gdzieś tam coś robi trochę więcej szkody, niż się spodziewamy. Więc im więcej takich side efektów jest, tym nasz quote łatwiej zdestabilizować.
Inną z takich większych metryk jest szybkość. Jeżeli mamy parę testów na krzyż, to nie ma to wielkiego znaczenia, jednak jeżeli chcemy mieć więcej tych testów, to powinny być szybkie. Jeżeli chodzi o unit testy, to powinno trwać ułamek sekundy. Nie powinno być tak, że mamy testy, które się wykonują parę sekund, bo jednak test, który będzie się wykonywał 10 sekund i będzie takich testów 1000, to już nie do końca to jest zwinne. One muszą przejść szybciutko, szczególnie, jeśli są gatem na PR-ce. Więc one powinny być szybkie.
Jak to zrobić? Trzeba wyłapać np. pewne zależności czasowe. Jeżeli mamy jakieś timery, to być może trzeba nasz test tak skonstruować, żeby tym czasem można było troszkę lepiej zarządzać w trakcie wykonywania testu. Przykładowo, jeśli ktoś używał Reactive Extensions (to w różnych językach jest dostępne, nie tylko w .NET), to tam jest do testowania tzw. wirtualna linia czasu, gdzie my możemy sobie tak jakby spłaszczyć ten czas, czyli zamiast czekać 10 sekund na wykonanie czegoś, to po prostu mówimy, że ten czas minął i teraz jesteśmy już w tym punkcie 10 sekund do przodu, i możemy już tę logikę wykonać.
Bo z naszego punktu widzenia nie jest istotne, że ten czas minął. Więc tutaj są techniki, które pozwalają nam lepiej to zorganizować, tak żebyśmy nie mieli tzw. waste’u, czyli bezsensownego czekania na coś, co w teście nie jest istotne. Chyba, że to jest meritum testu w jakiś sposób, ale to są już naprawdę takie niuanse.
Bardzo istotne też, by test był łatwy w utrzymaniu. Często naszym błędem jest to, że mamy ciągotki do kopiuj – wklej, i to nie tylko w testach. Po którymś razie stwierdziłem, że ctrl+c powinno być zablokowane, bo większość błędów z tego wynika. I tak samo jest w testach, często widzi się w testach takie sklejki z ctrl+c i ctrl+v. I teraz, na końcu dnia, miesiąca czy roku, jak zajrzymy sobie do tych testów, to mamy taką sklejkę. To jest taki kod, który nie jest poważny, nie traktujemy go jako część oprogramowania, tylko żeby tam się zapaliło na zielono, więc nie utrzymujemy tego i nie dbamy o odpowiednią higienę tego testu, jak on jest zorganizowany, czy jest czytelny.
Często testy dzielimy na takie trzy człony: Arrange, Act i Assert . I zwykle ta część Arrange jest niezbyt ładna, szczególnie jeśli stosuje się jakieś mockowania i nie zrobi się ich inteligentnie, nie rozłoży się ich jakoś ładnie, żeby napisać tzw. buildera. Jest taki patent Test Builder Patent, w którym można sobie jakoś to przygotować, tylko się robi takie po prostu kopiuj – wklej, tego mocka tutaj sobie w tym miejscu zbudujemy. Czasami budowanie takiego mocka to jest taki potworek, który ma parę linii kodu. I takich potworków parę musimy poskładać. I potem mamy taką sekcję Arrange na 20 linii, i potem ktoś wchodzi, robi drugi test, to robi kopiuj – wklej takiego potworka itd.
Więc jeżeli się tym dobrze nie zarządzi, to jest to straszna sprawa w utrzymaniu. Bo jeżeli nagle coś się zmieni w kodzie, to wszystkie te potworki musimy poprawić, więc to jest jednak dosyć istotna sprawa, żeby to było czytelne i żeby jak ktoś otwiera ten test, widział, co się tam dzieje. Żeby on się nie skupiał na tych technikaliach i żeby było wiadomo, co my w ogóle testujemy w tym teście.
Bo czasami wystarczy parę komentarzy w kluczowych miejscach, jeżeli nawet mamy pewne powtarzalne kawałki kodu, to możemy sobie gdzieś jakiś komentarz dać, że tutaj, to ustawienie, ta zmienna to meritum. Dobre komentowanie nie jest tylko w tzw. kodzie produkcyjnym, ale też w testach. Jeżeli zrobimy sobie dobrze okomentowany test, to od razu widzimy, co się dzieje, a jeśli jeszcze dodatkowo go dobrze nazwiemy, to już w ogóle bajka – od razu widać, co testujemy i gdzie jest meritum tego testu.
Jeszcze jest jedna kwestia. Test ma jasno mówić, jaki to jest scenariusz testowania. Jak sobie wejdziemy w taki plik z wieloma testami i szukamy jakiegoś jednego scenariusza, który nas interesuje, to też powinniśmy umieć go znaleźć. Więc jeżeli tam jest jakiś bałagan i jest to nakładane w trybie szpachlowym, jak ja to mówię, używając nomenklatury samochodowej, to też musimy się tam dobrze odnaleźć.
Możemy też się uzależnić od takich niestabilnych kawałków kodu, jeżeli chodzi o oprogramowanie, np. czasu. W .NET jest takie coś, jak Data.now, w każdym języku można pobrać bieżący czas. I jeżeli się uzależnimy od takiego czegoś, to jest też taka tykająca bomba. Bo możemy sobie zrobić test, który uruchomi się przed północą, skończy po północy, i różne takie tricki, dni przestępne czy cokolwiek innego sobie wymyślimy, i nagle się okaże, że bach! – nasz test nie działa. Więc nad czasem też musimy panować, więc żadne Data.now.
Chciałbym się odnieść do jednego aspektu, mianowicie do szybkości, którą przedstawiłeś jako ważny przymiot dobrego testu. Myślę, że wiele z tym elementów, o których wspomniałeś, de facto sprowadza się na koniec do poprawienia szybkości, zwłaszcza jeśli to jest element tego pipeline CI często uruchamiamy takie testy i zależy nam na tym, żeby one były w miarę szybkie.
Ma to znaczenie zwłaszcza, jeżeli idziemy sobie gdzieś tam w górę tej piramidy testowania, bo wtedy skupiamy się nie tylko na jakimś małym fragmencie kodu, który sobie unit testem, powiedzmy, ograliśmy, ale już musimy współpracować z innymi systemami.
I żeby poprawić trochę tę szybkość, często stosujemy różnego rodzaju atrapy, które nam symulują zachowanie różnych systemów, z którymi chcemy się komunikować, nad którymi nie mamy gdzieś tam kontroli albo zależy nam na ograniczaniu wykorzystania zasobów. No właśnie, wykorzystujemy tzw. mocki, tak jest bardzo wiele typów tych atrap, ale umownie nazwijmy je mockami.
No właśnie, teraz chciałbym Cię zapytać, jak do tego tematu podejść z głową, jak wykorzystywać odpowiednio te mocki, ale z drugiej strony też nie rozmydlić za bardzo tego obrazu, że tu de facto wszystko będzie jedną wielka atrapą.
To jest chyba tak naprawdę jeden z najtrudniejszych aspektów testowania. Mocki czy mockowanie, czy ucinanie pewnych części, zastępowanie ich jakimiś atrapami – najtrudniejsze w tym jest to, żeby zrobić to dobrze. Bo nie są problemem technikalia, jesteśmy na tyle dojrzali, że mamy już jakieś tam swoje tuningi, swoich pomocników do pracy, mamy narzędzia do tego, jesteśmy już parę lat na rynku w branży IT, więc mamy te możliwości, możemy to zrobić, odizolować, nie musimy strzelać do bazy danych, możemy sobie ją zamockować itd.
Tylko że tutaj prawdziwa sztuka to dobre wyważenie tego, kiedy coś odciąć, a kiedy nie. I przez pewien czas, dopóki nie przeczytałem tej książki, o której wspomniałem, nie wiedziałem, jak to nazwać. Autor nazwał to szkołą londyńską. Mówi ona o podejściu do testowania per klasa. I mamy klasę, testujemy ją, i wszystkie zależności, które ma klasa, mockujemy . Tak po prostu.
I nie raz to się sprawdzało, ale po jakimś czasie stosowania tej metodologii kończymy z tym, że przez większość czasu naszego testowania piszemy mocki. Chcemy napisać coś, to musimy omockować wszystko, cały świat dookoła. Druga sprawa, że jeżeli używamy .NET, gdzie by design metody nie są wirtualne (możemy oczywiście je zrobić wirtualnymi, ale nie są), to zwykle stosuje się interfejsy i wszystko jest za interfejsem i każda klasa ma interfejs. Równie dobrze można by powiedzieć, że interfejy mogłyby być generowane z automatu, bo w teorii wszędzie wszystko musielibyśmy postawić za interfejsem. I po paru miesiącach czy latach zapala nam się po raz kolejny żółte światełko, że coś tu jest nie tak.
Mam taki dłuższy cytat, nie będę go tu przytaczał, ale zapraszam do siebie na bloga, bo jestem w trakcie publikowania artykułu właśnie w tematyce testów, ale w skrócie autor takiej biblioteki jak StructureMapi innych fajnych rozwiązań, Jeremy Miller w jednym ze swoich artykułów napisał właśnie takie podsumowanie, że przez wiele lat pisał te mocki i w końcu stwierdził, że coś tu jest nie tak, to mockowanie całego świata nie do końca mu się sprawdza. Bo jednak na końcu przy każdym przedsięwzięciu powinniśmy sobie zrobić jakieś podsumowanie, jak w Agile czy Scrumie, jakieś przemyślenie, czy to, co robiliśmy trzyma się kupy.
Oczywiście ja uważam, że każdy powinien mieć refleksję, tzn. nie lubię podejścia biblijnego, że mamy świętą księgę programowania, świętą księgę Scrum i róbmy tylko tak, bo inaczej przyjedzie do nas policja DDD i nam powie, że coś źle robimy. Więc tutaj podobnie jest w tej materii, trzeba mieć pewną refleksję, spojrzeć sobie i zobaczyć, jaka jest wartość tych naszych mocków i czy naprawdę są one potrzebne.
I jest też druga szkoła, która mówi o tym, że nie testujemy klasy, a testujemy use case. Różni się to tym, że nie musimy mockować wszystkiego, tylko jeżeli testujemy jakiś use case to po prostu przekazujemy tam konkretne instancje klas, które testujemy. Czyli my testujemy jakiś tam kawałek ekosystemu, ja nie mówię, żeby testować całość, ale jeżeli mamy jakiś use case biznesowy (i to, co teraz powiem, tyczy się tej części biznesowej), powołujemy do życia ileś tam klas (te, których tak naprawdę potrzebujemy), mockujemy tylko to, co jest naprawdę zewnętrzne dla naszego biznesu. Czyli jeżeli te kawałki na 10 klas jedna pisze do bazy danych, to może tam możemy uciąć, przynajmniej jeżeli chodzi o testy jednostkowe. Ale zamiast pisać osobne testy do tych 10 klas, mamy testy w ramach tego use case’a i to testujemy.
I to się upraszcza, nie mamy tych interfejsów, nie mamy mocków, po prostu mamy czyste klasy biznesowe i to testujemy. I to podejście naprawdę jest fajne, tylko oczywiście jak zawsze jest jakieś „ale”. Tak jak powiedziałem, żeby nie było świętej biblii, że to jest jedyne słuszne podejście, to zawsze w naszym biznesie ważny jest kontekst. Wszystko zależy od tego, co testujemy.
Jeżeli jesteśmy w domenie jakiejś biznesowej, to tak, to jest clue naszego biznesu. Ale może naszym biznesem jest coś, co ma więcej infrastruktury i tam lepiej potestować i nfrastrukturalnie, może testy integracyjne są dla nas lepszym clue niż testy jednostkowe. Ja ostatnio musiałem pisać testy w PowerShellu, to jest zupełnie inny, dynamiczny język, więc tam też trochę inaczej się testuje i pewne rzeczy, które w C Sharpiesą bez sensu, tam może jakiś sens jednak mają, bo jest inne środowisko itd.
Więc tak naprawdę zawsze ważny jest ten kontekst, w ramach którego to robimy. Ale mockowanie wszystkiego sprawia, że możemy się zakopać w tych mockach. Więc zachęcałbym do tego drugiego podejścia, żeby troszkę się pozbyć tych mocków i zobaczyć, że da się pisać testy z ich mniejszą ilością.
To jeszcze może w duchu tej retrospekcji, o której powiedziałeś, chciałem Cię zapytać, czy znasz, czy może widziałeś jakieś code smells, czyli takie przykłady brzydko pachnącego kodu, jakby to bezpośrednio przetłumaczyć, związane z testowaniem, które mogą nam powiedzieć, że może to już nie iść w dobrą stronę. Jednym słowem mówiąc, czy są jakieś antypatterny, coś, co często robimy źle, co powtarzamy, co przekłada się na to, że to nasze testowanie nie jest optymalne?
Z tych rzeczy, które powiedziałem wcześniej, to troszkę się to automatycznie przekłada. Czyli jeżeli mamy testy, które są niestabilne, jeżeli nam coś wybucha raz na jakiś czas, to coś jest nie tak. Jeżeli często musimy te testy zmieniać, bo cokolwiek ruszymy w naszym systemie, to musimy połowę testów zmienić, to jest takie światełko, że jednak coś tam jest nie tak, coś nie jest dobrze porozdzielane, coś te testy źle testują. Więc to jest ta stabilność, o której mówiłem.
Jeżeli z kolei zbyt dużo czasu poświęcamy na te testy, jeżeli one nie służą nam pomocą, tylko są kulą u nogi z jakichś powodów, to jest różowe światełko. Bo czerwone światełko jest wtedy, kiedy dzieje się coś, że w pewnym momencie zapada decyzja, że wyłączamy te testy. Bo za dużo czasu nad nimi spędzamy, bo coś się nam ciągle wysypuje. Więc nad tą stabilnością testów trzeba pracować od początku i już przy każdym żółtym światełku zastanowić się, czy my na pewno dobrze to testujemy.
Na pewno to, że coś trwa za długo. Bo musimy też spojrzeć na to, jak deweloperzy pracują, jaki mają tryb pracy. Bo jeżeli mamy, powiedzmy, kogoś, kto pracuje nad jednym zadaniem, to on sobie zrobi coś, wypuści kawałek kodu i ma pół godzinną PR-kę, tzn. teraz jest trochę inaczej, bo kiedyś było tak, że pracowaliśmy w biurach, teraz trochę mniej się pracuje w biurach, ale to się wychodzi na kawkę albo gdzieś tam. Mógłbym wziąć kolejne zadanie, ale niekoniecznie muszę. Więc pójdę sobie, wrócę za 20 albo za kolejne 20 minut. Albo inny kolega sobie czeka też na tę PR-kę, bo pracujemy w parę osób, więc też nie robi, czeka, jest mnóstwo waste’ów i ta integracja tego kodu się wydłuża, jak to wszystko się zliczy. Więc te sprawne testy nie tylko dają nam szybką odpowiedź, czy jest coś okej, czy nie, ale też poprawiają nam sprawność integracji tego kodu do naszej głównej linii kodu. To jest dosyć istotna sprawa.
Co jeszcze jest takim światełkiem? Na pewno, jeżeli musimy komuś wytłumaczyć, co robi ten test. To znaczy, że on jest źle napisany. Tak samo jak z kodem i ze wszystkim. Jeżeli test jest dobry, dobrze napisany, czytelny, to można go komuś pokazać i on od razu widzi, co to za test. Generalnie w naszej branży mówi się, że są dwie trudne rzeczy: inwalidacja casha i nazywanie. Jeżeli chodzi o nazewnictwo, to można się wręcz pośmiać nieraz, jak ludzie się przepychają, jak coś nazwać.
Słyszałem dużo różnych rad, ostatnio u nas w zespole robimy tak, że nie nazywamy testów w samej nazwie metody, tylko jest w atrybucie opisowo, po ludzku piszemy, w xUnit jest coś takiego, jak Description i sobie można takie coś zrobić. I teraz, jeżeli ktoś sobie spojrzy i ma napisane, że ten test robi to, to i to, druga sprawa, że ma jakieś tam komentarze w kluczowych miejscach, co się gdzie dzieje, że w tej czy tej linijce mamy kluczową rzecz itd., i ten kod jest na jeden ekran, a nie na 20, że musimy go przescrollować, to ktoś wchodzi, zatrudniamy nowego programistę, on sobie patrzy i widzi, co tu się dzieje, co trzeba poprawić, bo test czytelnie pokazuje, co jest źle.
Nie wystarczy powiedzieć, że error. Test nie wykonał się. Nieraz może to wystarczy, jeżeli mamy dobrze opisane itd., ale jednak opisowość Assertów też jest czasami istotna. Zawsze warto robić tę retrospekcję.
I jeżeli idziemy takim trybem red-green-refactor, czyli na początku piszemy coś, żeby się wywaliło, później to naprawiamy i robimy refaktor i żeby się zapaliło na zielono, to zwracajmy uwagę na to, jak to się wywala – żeby test tego, co się źle dzieje powiedział nam, co jest nie tak. Tak samo, jak w logach logujemy, też musimy patrzeć, co tam się dzieje, żeby później wiedzieć, co z tym błędem zrobić. Bo jeżeli właśnie logujemy źle, czy do logów, czy w testach, to to jest tak naprawdę do niczego. Później czy my, czy ktoś inny musi sobie przypomnieć, o co chodziło w tym kodzie, i im lepiej, czytelniej jest napisany, tym łatwiej będzie od razu wiedzieć, co się dzieje. Więc ta czytelność na pewno jest istotna.
Ostatnia kwestia jest związana z wyłączeniem testów, to jest już taka ostatnia śmierć testów. Chodzi o to wyłączanie testów, żeby PR-ka nam przeszła. Kiedyś to się włączy i jest problem. Z autopsji wiem, że nie warto zostawiać czegoś na koniec, a te wszystkie rzeczy, które wymieniłem, to są takie żółte światła ostrzegające, że coś się źle dzieje i że trzeba od razu interweniować.
Z tych rzeczy, które powiedziałem wcześniej, to troszkę się to automatycznie przekłada. Czyli jeżeli mamy testy, które są niestabilne, jeżeli nam coś wybucha raz na jakiś czas, to coś jest nie tak. Jeżeli często musimy te testy zmieniać, bo cokolwiek ruszymy w naszym systemie, to musimy połowę testów zmienić, to jest takie światełko, że jednak coś tam jest nie tak, coś nie jest dobrze porozdzielane, coś te testy źle testują. Więc to jest ta stabilność, o której mówiłem.
Jeżeli z kolei zbyt dużo czasu poświęcamy na te testy, jeżeli one nie służą nam pomocą, tylko są kulą u nogi z jakichś powodów, to jest różowe światełko. Bo czerwone światełko jest wtedy, kiedy dzieje się coś, że w pewnym momencie zapada decyzja, że wyłączamy te testy. Bo za dużo czasu nad nimi spędzamy, bo coś się nam ciągle wysypuje
Wcześniej podawałeś kilka różnych przekładów ze świata . NET w kontekście dobrych praktyk testowania, to może teraz byś przywołał kilka narzędzi, bibliotek, rozwiązań z tej domeny .NET, które wg Ciebie warto polecić w kontekście testowania.
Zacznę może od standardów. Standardowo musi być na pewno jakieś narzędzie do pracy deweloperskiej. Visual Studio Rider czy Visual Studio Code powinno nam wystarczyć, nigdy nie potrzebowałem niczego więcej. Ewentualnie do linii komend .NET SDK. To jest taki standard, jeśli chodzi o narzędzia. Jeśli chodzi o biblioteki, to zawsze mamy jakiegoś xUnita albo NUnita, zwykle tego się używa i do tego dochodzi jeszcze Moq do mockowania albo jakiś tam Fake Ii, ale chyba Moq jest najbardziej popularne. Więc to jest taki standardowy pakiecik, który wszędzie występuje w .NET.
Z takich dodatkowych rzeczy, które mógłbym polecić, to na pewno biblioteka, która się nazywa Fluent Assertion i ona pozwala na łatwiejszą asercję tego, co robimy. Ma taką składnię fluent. Czyli jeżeli mamy taki ciągły wywołań metod, to wtedy nazywa się to fluent i wtedy możemy sobie powiedzieć, że kod powinien spełniać jakieś tam założenia i to Fluent Sessionwystępuje do wielu bibliotek, możemy go razem z xUnitem czy NUnitem wykorzystywać. I to ułatwia pisanie tych asercji, nie musimy czasami ręcznie pisać tego kodu.
Drugą, powiedzmy, że biblioteką, bo jeżeli ktoś pisze serwisy w ASP .NET Core, to nie wszyscy zdają sobie sprawę, że jest tam wbudowana możliwość testowania integracyjnego. I to jest bardzo fajna sprawa. Pozwala to napisać test integracyjny, który jest i memory i nie potrzeba za bardzo tego naszego serwisu sztucznie stawiać gdzieś tam na boczku, tylko on nam wszystko zrobi. Memory, wszystkie odwołania web API, więc to jest bardzo fajna sprawa, jeżeli chodzi o testowanie takich serwisów w .NET Core.
Warto też pamiętać o jakichś gate’ach. Fajnie, że mamy te testy, one się tam zapalają na zielono, mamy jakiś code cover, fajnie, że jest, ale istotną sprawą jest też to, żeby mieć jakiegoś rozsądnego gate’a, który nas zatrzyma. I przykładowo może to być Sonar albo jakieś inne narzędzie, jeśli mamy np. GitHuba czy jakieś inne narzędzie do naszej integracji. Może to być np. SonarQube .
I jeżeli ktoś trochę bardziej bawi się w testy serwisów web API , to jeszcze bym polecił Pact-Net, który służy do testowania kontraktowego. Nie będziemy tu wchodzili może w szczegóły, ale to też jest fajna możliwość do testów.
Mówiliśmy dzisiaj głównie o perspektywie dewelopera na testowanie. A gdybyśmy mogli nieco szerzej potraktować tę perspektywę, to jak byś określił, jak wykorzystuje się tę wiedzę, o której tutaj powiedziałeś, w mBanku? Jakie role są zaangażowane w testowanie oprogramowania?
Tutaj generalnie staramy się z roku na rok polepszać jakość i podchodzić poważnie do tego testowania. Jest tego parę aspektów. Jedna kwestia to jest szerzenie tej wiedzy. Żeby to, co teraz powiedzieliśmy tutaj, troszkę krążyło po tych deweloperach. Jakieś wpisy na Wicki, jakieś szkolonka itp. Żeby ludzie byli bardziej świadomi tego, jak mają pisać te testy, bo to jest to, co powiedziałem na początku, że dużo się mówi o tym, żeby te testy były, ale one muszą też mieć jakąś jakość.
Druga sprawa, to że mamy osobną komórkę w mBanku, która spotyka się z zespołami, robi z nimi jakieś review, jak oni testują, ile tych testów mają, jakie itd. Więc każdy zespół jest wzywany na dywanik, jest sprawdzana jakość. To nie chodzi o to, żeby się bać, ale żeby mieć z tyłu głowy, że jednak ktoś na to patrzy. Bo jeżeli ktoś wie, że nikt na to nie patrzy, to powoli te testy umierają, więc trzeba o to dbać. Czyli nauczać i pilnować. Bo jednak pilnować zawsze trzeba, jak wszędzie.
Dominik Jeske z mBanku był moim gościem, rozmawialiśmy o dobrych praktykach w testowaniu na przykładzie .NET.
Dominik, bardzo Ci dziękuję za Twój czas i za podzielenie się swoją wiedzą.
Dzięki. Zapraszam na mojego bloga, tam znajdziecie więcej informacji w tym temacie – mam akurat serię wpisów.
To idealne rozszerzenie do naszej rozmowy. Oczywiście namiar na Dominika, jak i na jego bloga będzie w notatce do odcinka.
Dominik, jeszcze raz dzięki za poświęcony czas.
Do usłyszenia, cześć!
Dzięki! Do usłyszenia, cześć!
I to na tyle z tego, co przygotowałem dla Ciebie na dzisiaj. Po więcej wartościowych treści zapraszam Cię do wcześniejszych odcinków. A już teraz, zgodnie z tym, co czujesz, wystaw ocenę, recenzję lub komentarz w aplikacji, w której słuchasz lub w social mediach.
Zawsze możesz się ze mną skontaktować pod adresem krzysztof@porozmawiajmyoit.pl lub przez media społecznościowe.
Ja się nazywam Krzysztof Kempiński, a to był odcinek podcastu Porozmawiajmy o IT o dobrych praktykach w testowaniu na przykładzie .NET. Zapraszam do kolejnego odcinka już wkrótce.
Cześć!