Testowanie modularne a trwałość kodu

 

Dzisiaj chciałem opisać jednostkowe testy modułowe w kontekście rozmów które zdarza mi się prowadzić w różnych projektach. Wydawałoby się że A.D. 2021 nie powinno być dla nikogo zaskoczeniem, że klasyczna architektura warstwowa w przełożeniu na podejście do testów jednostkowych gdzie jako jednostkę rozumie się klasę testowaną w połączeniu z próbami modularyzacji również odzwierciedlającą strukturę warstw to trochę jak wspomnienie dziwnych zwyczajów ludności zamieszkującej Suwalszczyznę w epoce wczesnego neolitu. 

Przede wszystkim zbyt granularny podział na moduły komunikujące się nieustannie nie ma większego sensu o czym już pisałem w tekście o typach architektury. Wymienialność elementów również nie jest argumentem przemawiającym za separowaniem każdej technologii do osobnego modułu jeśli jest warstwa tak dobrze przygotowana jak np. bazy danych w springu, gdzie nawet nie trzeba implementować warstwy dostępowej i wystarczy jej zadeklarowanie w interfejsie. Cierpienie junior developerów w piekle takiej heksagonalnej architektury słychać przy wyjściu na kawę - "i jak kurła ja mam to teraz otestować?". Osobne testy od zalania, osobne od plag, od głupoty ludzkiej uchowaj nas Panie. Takie modlitwy słyszę, na prawdę. Przypadkowa złożoność jest jednak drugą naturą "doświadczonych" programistów którzy wiedzą że kod to cierpienie i wtedy smakuje jak boli. Mnie nie kręci włosienica i samo-batożenie w rytualnym modlitewnym codziennym głoszeniu litanii do bóstwa cebulowego monolitu sprzed kilku dekad rozbijanemu na moduły technologiczne.

Idźmy więc w naszym rozważaniu nieco w stronę testów modularnych. Nie piszemy już testu do każdej klasy - klasa to detal implementacyjny, dziś taki jutro inny. To, że ten proceduralny kod znajduje się w jakimś serwisie to tylko kwestia organizacji kodu i rozbicia go na mniejsze elementy. Wszak wiemy, że koledzy piszą to w kontrolerze i też działa :) Nie chodzi więc o to gdzie jest ten kod i czy ktoś wyciągnął proces do innego serwisu lub klasy, czy stał się on cechą samego obiektu na którym operuje. To moda. Dzisiaj jest model anemiczny jutro model bogaty. Testy powinny mieć na to mówiąc kolokwialnie wywalone. Testy powinny cementować wymagania, historyjki użytkownika i kryteria akceptacyjne, a nie to gdzie komuś zajawiło się nagle by napisać ten kod. To są rzeczy związane z konsensusem zespołu oceniane na code review. Test powinien testować czy kod działa, a ie to jak jest zaimplementowany! W kontrolerze też zadziała i dobrze.

Druga rzecz - pokrycie testami. Jeśli test modułowy parametryzowany nie jest w stanie jakiegoś kawałka kodu pokryć oznacza to jedno. Wywal to! To kod martwy który unit testami pokryjesz i który będziesz utrzymywać w przekonaniu, że to ważne. Samemu nie raz tak znalazłem jakieś starorzecza i boczne odnogi czasu zapomniane jak niektóre epiozody "Sanatorium pod klepsydrą" (przy okazji polecam film https://www.cda.pl/video/741815271).

Wymienialność technologii to coś co w podejściu modułowym też jest świetne bo możemy wymieniać je moduł po module a nasz jednostkowe testy będą nadal działać bo nie testują zewnętrznych serwisów, baz danych i kolejek - mają własne implementacje. Technologie będziemy testować kilkoma testami integracyjnymi gdzie sprawdzimy kontekst Springa czy połączenie do Kafki, albo kilka specyficznych dla konkretnej wersji postgresa zapytań bazodanowych, ewentualnie złożone zapytania SQL które piszemy w kodzie jeśli chcemy wiedzieć czy kod pisany jakimś DSL na pewno działa. Testy modułowe traktują cały moduł jako jednostkę ze swoim wejściem i wyjściem. Są odporne na "pierdołowate" zmiany o ile nie zmieniają one wyniku końcowego uzyskiwanego przez nasz kod, a są poprawkami do defektów zgłaszanych przez narzędzie do statycznej analizy kodu, lub kiedy kod rozrastającego się serwisu delegujemy w inne miejsca by ukryć detale pod abstrakcją wywoływanej metody, co zawsze zwiększa czytelność kodu (o ile nazwy metod nie kłamią).  


Comments