Pokochać Unit Testy – typowe problemy



Mój znajomy bardzo entuzjastycznie podchodził do kwestii jakości a w szczególności był fascynowany testami jednostkowymi. Jednak musiał się ciągle zmagać z typowymi problemami dotykającymi unit testy. Widział jednak, że testy te są niezmierne ważne i nie ustawał w próbach znalezienia na nie rozwiązania. I znalazł! Przyszedł uśmiany i mówi:
- To przez tamte stare narzędzie do testów było tak ciężko! Znalazłem nowe, lepsze narzędzie do tworzenia unit testów! Teraz pójdziemy jak burza!
Narzędzie było ładne, kolorowe. Przepisano testy.
Ku zaskoczeniu wszystkich – problemy wróciły!
Problemy nie wynikają z narzędzia, którego używamy. Ale ze sposobu, w jaki konstruujemy kod. Żadne narzędzie nie pomoże w testach kodu spaghetti, żadne narzędzie nie pomoże w konfiguracji skomplikowanego środowiska..

Także pamiętajcie:
Nie narzędzie lecz chęć szczera zrobi z ciebie unit testera!

Jak wspomnieliśmy w poprzednim wpisie Pokochać Unit Testy – pierwsze kroki testy jednostkowe są niezbędnym elementem procesu tworzenia oprogramowania. Dzięki unit testom jesteśmy w stanie znaleźć największą ilość błędów i w najkrótszym czasie je naprawić. Żadne inne testy nie mają nawet szans na osiągnięcie takiej wydajności.

Jednak to tylko teoria. W praktyce testy jednostkowe często okazują się bardzo skomplikowane, czasochłonne i niewydajne. Poświęcamy dużo czasu i wysiłku a w wyniku tego znajdujemy błędy w samych testach. Błędy zaś w aplikacji jak były tak są. Jest to bardzo zniechęcające i trzeba wyjątkowego uporu, by ciągle naciskać i kontynuować, jak najbardziej dobrą praktykę, pisania Testów Jednostkowych.

W większości jednak przypadków, rezygnuje się niestety z tego jak ważnego elementu programowania.

Ale czy na pewno jedynym wyborem jaki mamy to bardzo kosztowne testy lub ich brak?

Żeby móc wyeliminować problemy, najpierw musimy je zidentyfikować. Przyjrzyjmy się, z jakimi trudnościami programiści dbający o jakość muszą się zmagać.

Typowe problemy podczas tworzenia testów jednostkowych

Problem #1: Kod spaghetti
Długi i skomplikowany kod procedur jest bardzo trudny lub wręcz niemożliwy do przetestowania W kodzie spaghetti jest dużo pętli, klauzul warunkowych. Ilość ścieżek logicznych jest niewyobrażalnie wielka. Testowanie takiego kodu jest zbyt kosztowne w porównaniu do ewentualnych zysków.

Problem #2. konfiguracja środowiska testowego
Do przetestowania jakiejkolwiek funkcjonalności, często wręcz banalnej potrzebujemy kompletnego, skonfigurowanego środowiska testowego. Dodatkowo musimy przygotować parametryzację tak, by aplikacja wykonała proces konkretną ścieżką. Musimy doprowadzić do spełnienia wszystkich warunków, w którym aplikacja wykona naszą, testowaną, procedurę. Jeśli nasza procedura jest głęboko w procesie może nie być to łatwe zadanie. A co jest skomplikowane – jest czasochłonne i kosztowne.

Problem #3. przygotowanie danych testowych
W dużych systemach struktury danych są rozbudowane i mocno ze sobą powiązane. Przetestowanie konkretnej nawet niewielkiej funkcjonalności czy procedury komplikuje się, jeśli musimy skonfigurować całą hierarchię danych w naszej bazie. Często walczymy z przygotowaniem rekordów mających 100 czy więcej pól, które muszą do siebie pasować, przejść skomplikowane weryfikacje i walidacje Znów czas i koszty. Często o wiele przewyższające zysk sprawdzenia poprawności jakiegoś małego technicznego warunku testowego.

Problem #4. uzależnienie wyników procedur od czynników zewnętrznych
Wynik procedury często zależy od czynników zewnętrznych. Jako czynniki zewnętrzne mam na myśli wszystko, co nie jest parametrem wejściowym.

Jeśli w procedurze pobierane są dane konfiguracyjne – żeby mieć taki sam wynik testu – musimy zawsze tak samo skonfigurować całe środowisko testowe. Jeśli ktoś nam coś zmieni – wyniki testów będą niepoprawne, niezgodne z testem.

Bardzo często wyniki zależą od takich zmiennych jak bieżąca data systemu. Jeśli wynik funkcji zależy od takiej daty – powtórzenie testu wymaga ustawiania daty systemowej na całym systemie. Jeśli tego nie zrobimy uzyskamy błędne wynik testu.

Na wyniki testów bardzo często wpływają używane w procedurach zmienne globalne. Ustawienie takich zmiennych często umyka podczas tworzenia środowiska testowego albo nawet zmienne te są niemożliwe do jawnego ustawienia. W takich sytuacja przygotowanie właściwego testu jest czasochłonne i bardzo kosztowne lub wręcz niemożliwe.

Wyniki testu też mogą być "w locie" zmienione przez wyzwalacze, których nie jesteśmy w stanie kontrolować za pomocą parametrów wejścia / wyjścia, które mogą nam przekierować nasz test na zupełnie nieprzewidywane przez nas ścieżki.

Wygląda to niewesoło. Ale mam dobre wiadomości. Wystarczy wdrożyć w proces tworzenia oprogramowania kilka drobnych, prostych zasad – by wyeliminować WSZYSTKIE powyższe problemy!

Trzy złote zasady pisania testowalnego oprogramowania:
1. Piszmy małe, łatwe do przetestowania jednostki programowe (jak uniknąć kodu spaghetti)
2. Testujmy procedury funkcjonalne a nie procedury pobierające dane czy parametryzacyjne
3. Piszmy tak, by dla jednego zestawu parametrów wejściowych ZAWSZE otrzymać dokładnie taki sam wynik wyjściowy
- parametryzację przekazujmy parametrami wejściowymi
- dane przekazujmy parametrami wejściowymi
- unikajmy w procedurach funkcjonalny zmienny globalnych, zmiennych typu sysdate i triggerów!

Zastosowanie tych zasad spowoduje, że nasz kod będzie krótki, zwięzły i co najważniejsze – bardzo łatwy do przetestowania! Nie będziemy potrzebowali do testów konfiguracji środowiska, skomplikowanych danych ani bardzo skomplikowanej procedury testującej. Test zawsze będzie nam zwracał te same wyniki dla takiego samego zestawu danych! Czy to nie brzmi prosto i fascynująco? Spróbujcie sami. Różnica jest niewyobrażalna!

Gdy zobaczycie, że pisanie testów jednostkowych wcale nie musi być syzyfową pracą ani pochłaniać połowy kosztów budżetu – będziemy mogli spróbować nowoczesnych technik programowania TDD, obudować cały system testami jednostkowymi a na końcu przygotować testy automatyczne.

Czy wizja tanich i wydajnych testów jednostkowych prowadzących do wolnego od błędów oprogramowania nie jest kusząca? Stosując Dobre praktyki – da się. Wystarczy spróbować, do czego gorąco zachęcam!

Złote zasady w praktyce w następnych wpisach :)

Złote zasady:
1. testujmy procedury funkcjonalne a nie struktury danych czy parametryzację
2. piszmy niewielkie jednostki programowe
3. piszmy tak, by dla jednego zestawu wejścia - zawsze otrzymywać dokładnie ten sam wynik wyjściowy

Komentarze