Testowanie wielu pól

utworzone przez | 04/05/2020 | Test Driven Development

Ponoć kiedy kod jest już napisany nie da się stwierdzić, czy był pisany w modelu TDD, a jedynym sposobem na to, jest patrzeć programiście na ręce. Zasadniczo prawda, jednak jest kilka śladów zbrodni, które pozostają. Jednym z nich są testy sprawdzające zbyt wiele rzeczy na raz i zakładające, że czytelnik posiada wiedzę tajemną. Tak jak poniżej.

Albo tak: test_csv_export_correct, ale nie bądźmy aż tak okrutni 😉.

Jeśli to wygląda dla Ciebie legitnie, to proszę… nie przestawaj czytać, bo jest z tym kilka problemów.

Test „czy czy jest dobrze”

Przede wszystkim – co, do diaska, znaczy “all”?

Po tym wiem, że kodu nie napisano w modelu TDD. Nie zrozumcie mnie źle – “test first” jest tu całkowicie prawdopodobne. Ostatecznie nic nie powstrzyma Cię przed napisaniem takiego testu przed implementacją, ale TDD to nie będzie.

Dla przypomnienia: zamysłem programowania sterowanego testami jest by zawsze pisać najmniejszy nieprzechodzący test, który nadal przekazuje jakość informację (czyli assert False się nie liczy). Każde z 20 pól może nie istnieć i każde może mieć błędną wartość, więc mamy co najmniej 40 sposobów, na które ten test może nie przejść. Czyli za dużo się tu dzieje.

Ale jest z tym większy problem. Nie mogło to sterować programowaniem, bo nic to nie mówi o strukturze wymaganej CSVki. Zestaw testów w TDD jest specyfikacją – zestawem hipotez stawianych względem kodu.

Ktoś by się kłócił, że nazwa bez znaczenia, bo asercje mówią wszystko. Zgoda, ale skąd wiemy, że są poprawne…?

Kto pilnuje pilnujących?

Witamy odwieczne pytanie – jak testować testy? No testować się nie da, ale można je zwalidować porównując ciało z nazwą, a żeby się dało, to nazwa musi zawierać informacje. To jest tym ważniejsze, kiedy kod ewoluuje w czasie, bo nikt nie ruszy testu, który reklamuje się jako sprawdzający wszystko. Wszystko to wszystko, po co drążyć temat?

Więc jak powinno to wyglądać? Cóż, na początek każde pole powinno być testowane osobno. W zasadzie, każde założenie dotyczące każdego z pól powinno być testowane osobno, skąd wzięła się mantra “jeden test – jedna asercja”. Jest ona, niestety, trochę myląca, bo w praktyce jedno, niepodzielne założenie może wymagać 2 czy 3 asercji, zależnie od języka programowania, jednak kiedy masz ich 20 to coś tu śmierdzi.

Otrzymujemy więc coś takiego:

Zauważ jedną rzecz: nie potrzebujesz ani kawałka ciała testu, żeby załapać o co w nim chodzi. Nazwa w zupełności wystarczy. To znaczy, że masz jak sprawdzić, czy ciało testu zgadza się z intencją.

Możesz się też sporo dowiedzieć o architekturze i kontraktach. Przykładowo, system mądrze używa dedykowanego UUID, zamiast ID z bazy, do komunikacji ze światem. Po drugie, imię powinno być napisane dużą literą, więc lepiej żeby była na to asercja!

Nie wszystko na raz

Najczęściej wątpliwości względem takiego schematu wynikają z nietestowania wszystkiego jednocześnie, więc nie wiadomo czy te założenia są spełnione razem. Ale czy faktycznie? To jak powiedzieć, że all(foo) to nie to samo co foo[0] and foo[1] and … and foo[n].

Tym niemniej, musisz zapewnić, że input (inaczej given, inaczej arrange) i operacja (inaczej when, inaczej act) będą te same we wszystkich przypadkach. Wtedy deterministyczna natura komputera załatwi resztę.

Tu właśnie przydają się fikstury i setUp. Jednocześnie jest to chyba jedyny przypadek, kiedy DRY ma sens w testach – kiedy musisz upewnić się, że warunki początkowe i operacje będą spójne podczas testowania różnych założeń.

Użyjmy więc fikstury w pytest:

Rozdziały w testach, nie tylko w książkach

Już jest nieźle, ale kontynuujmy refactoring. Pewnie zauważasz, że mamy tu dwa schematy w nazwach. Pogrupujmy więc teksty zgodnie z tymi schematami:

Uwaga: Celowo psuję tutaj PEP8 i reguły długości linii i mam nadzieję, że za chwilę stanie się jasne dlaczego pomaga to zwiększyć czytelność testów, które rządzą się nieco innymi prawami niż klasy i metody, które odpalamy w kodzie.

Słyszę Cię, pyteście!

Teraz zobaczmy jak by wyglądało wywołanie jednego z testów pytestem:

$ pytest test_csv_export.py::Test_field::test_id_contains_user_external_uuid

Pomijając obligatoryjny prefiks test_ i kilka zignorowanych reguł gramatycznych, otrzymujemy niemalże poniższe zdanie:

CSV export field ID contains the user’s external UUID

Co jednak ważniejsze, tak samo będzie to wyglądało w “głośnym” wypisie z odpalenia testów, więc od razu dowiesz się co konkretnie nie działa, bez konieczności patrzenia na linie kodu czy wstawiania breakpointów do testu. Czyż to nie cudowne?

Testy jako dokumentacja

Podsumowując, jeśli chcesz mieć opisowe, pomocne i samo-weryfikujące zestawy testów, to upewnij się, że ich nazwy są konkretnymi hipotezami. Unikaj ogólnikowych słów w rodzaju “all”, “complete” czy “correct”. I nie martw się, że nie testujesz wszystkiego w ramach jednej funkcji – determinizm się tym zajmie.

O autorze

O autorze

Piotr Podgórski

Praktyk TDD, bez którego nie wyobraża sobie pracy nad czymkolwiek. Propagator metodyk lekkich i zwinnych, bywalec Zwinnej Łodzi. Nie straszna mu praca z kodem legacy, gdzie przyświeca mu odwracanie zależności i myśl Davida Wheelera: Every problem in computer science can be solved by another layer of indirection.

Czysta architektura okiem ekipy migawka.it

Jakie problemy napotkaliśmy na początku? W czym nam pomogła? Kiedy na pewno z niej nie skorzystamy?

Nie przeszkadzaj mi! Jak nie dać się wyrwać z flow

Ile razy ostatnio zostałeś brutalnie oderwany od swojej pracy? Zaszywasz się w biurze i nagle pojawiają się oni – nieoczekiwani goście i ich pytania, które nie niosą ze sobą grozy awarii albo powagi hotfixa. Ale są to pytania, które teraz, dokładnie w tym momencie muszą paść i oderwać cię od bieżącej pracy. Jak sobie z nimi poradzić?

Precyzyjne twoje wiadomości być muszą

Dostajesz wiadomość e-mail i zastanawiasz się, czy to na pewno do ciebie i co właściwie masz zrobić. Wysyłasz pilną informację na komunikatorze i czekasz tydzień na odpowiedź. Dlaczego? Przecież wszystko było jasne. Najwyraźniej jednak nie było. Chwila moment i dowiesz się wszystkiego.

Te bezsensowne interakcje przerywające pracę

Ile razy byłeś poirytowanie faktem, że musisz w pięciu mailach wyjaśniać o co chodzi? Ile razy miałeś wrażenie, że banalne tematy ciągną się w nieskończoność, a ty czułeś się jak niezastąpiona jednostka centralna, bez której nic w zespole się nie zadzieje? Oczywiście wszystko kosztem twojej efektywności i nieustannego przerywania własnej pracy. Posłuchaj pewnej historii i naucz się prostej techniki, która wyeliminuje zbędne interakcje.

Praca zdalna – komunikacja na czacie

Jak przenieść codzienne rozmowy z biura do świata online? Co z bezcennymi dyskusjami w kuchni? Najczęściej wrzucamy całą firmę na jakiś komunikator i liczymy, że zadzieje się magia. Nic bardziej mylnego i dziś opowiem ci jak sobie z tym poradzić.

Praca zdalna – jak nie zwariować?

Pandemia COVID-19 zmieniła wszystko. Większość z nas pracuje zdalnie. Pomożemy wam odnaleźć się w tej nowej rzeczywistości. Mamy doświadczenie w pracy „po kablach”, którymi chcemy się podzielić. Co ważne, choć to doświadczenie pochodzi z IT, to można je zastosować do każdej branży.

Prześlij komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Pozostań w kontakcie