Testy testów

Testy funkcjonalne nawet całej aplikacji nie wystarczą do osiągnięcia zadowalającej jakości oprogramowania.

Testy funkcjonalne nawet całej aplikacji nie wystarczą do osiągnięcia zadowalającej jakości oprogramowania.

Jednym z filarów extreme programming jest testowanie - w przypadku tej metodologii niemal 100% funkcjonalności kodu powinno przejść odpowiednie próby. Wynika to oczywiście ze specyfiki tego stosunkowo nowego sposobu programowania. Jednakże nawet w tradycyjnych, bardziej uporządkowanych metodologiach do testów przywiązuje się coraz większą wagę. Czy to extreme programming czy nie, zasadą staje się, że testowanie należy zorganizować tak, by zarówno pisanie testów, jak i ich uruchamianie było procesem niemal automatycznym, nie obciążającym zbytnio programisty.

Wbrew złośliwości kodu

Nawet jeżeli moduł czy pojedyncza funkcja zostały solidnie przetestowane, ale w tym czasie zmieniono inny fragment programu, istnieje ryzyko, że modyfikacja nie pozostanie bez wpływu na działanie całej aplikacji. Może się bowiem okazać, że po zmianach w pozornie odrębnej części programu, sprawdzony uprzednio moduł czy funkcja działają inaczej niż planował autor programu. Stąd zaleca się, by testy były tak skonstruowane, aby za każdym razem można było uruchamiać zestaw procedur sprawdzających. Innymi słowy kody funkcjonalny i testowy muszą być bardzo blisko powiązane. Test powinien być automatycznie wywoływany wg odpowiedniej kompilacji czy linkowania.

Bodajże najpopularniejszym narzędziem tego typu są moduły xUnit, związane z Test Driven Development (część extreme programming). Jest to tzw. do tworzenia procedur testowych, opracowany dla znakomitej większości używanych współcześnie języków programowania. W wielu przypadkach powstają na bazie xUnit gotowe aplikacje upraszczające generowanie testów. Przykładowo, w przypadku Delphi czy C++ Builder można z poziomu środowiska IDE tworzyć szkielety klas testowych.

Pisanie testów analizujących różne aspekty użycia określonej funkcji jest kłopotliwe. Obecnie jest opracowywane specjalne narzędzie przeznaczone dla języka Java, które na podstawie interfejsu opisującego zachowanie konkretnego elementu języka wygeneruje kod testujący. Zachowanie funkcji określa się przy użyciu specjalnego języka JML - Modeling Language. Funkcje poprzedzają specjalne sformatowane komentarze zawierające opisy np. warunków ograniczających zwracaną wartość, sytuacje gdy dopuszczalne jest zgłoszenie wyjątku. W niedalekiej przyszłości za pomocą JML będzie można podać konkretny przypadek użycia danej funkcji. W języku tym są definiowane rozbudowane asercje, które potem mogą być przekształcone w szkielety modułów testowych zgodne ze specyfikacją JUnit.

JML ma pozwolić na realizację założeń Design by Contract (podejście to, zaczerpnięte z języka Eiffel, ma doprowadzić do powstania bezbłędnego oprogramowania). Można określać warunki, jakie musi spełniać klient wywołujący daną funkcję, jaki ma być stan metody po wykonaniu operacji, jakie asercje mają być spełnione w określonych punktach programu. Test wygenerowany z wykorzystaniem JML wysyła komunikaty do obiektów Javy i określa, kiedy operacja zakończyła się niepowodzeniem (czy zdefiniowany przy użyciu JML sposób działania programu został naruszony). Niestety, dane testowe musi utworzyć ręcznie programista.

Testy w środowisku

Przy rozważaniach na temat testowania nie sposób pominąć różnic w podejściu do tego zagadnienia, związanych ze specyfiką środowiska, w którym aplikacja jest opracowywana i tu są też wykonywane testy. Należy podkreślić - wszystkie testy.

Dla Javy opracowano niemal wzorcowy framework do testowania - JUnit, (produkt open source, obecnie rozwijany głównie przez ObjectMentor). To zestaw klas, które muszą wykorzystywać moduły testowe. Odrębne narzędzie wywołuje poszczególne moduły i analizuje, czy test zakończył się sukcesem.

Obecnie na bazie JUnit jest opracowywane narzędzie, które testuje wydajność (prędkość działania i zużycie zasobów) na podstawie procedur zawartych w kodzie przeznaczonym dla JUnit. Narzędzie JUnitPerf symuluje wielokrotne wywołanie procedur testowych, przestawia kolejność wywoływania określonych funkcji itp. Nie jest to pakiet przeznaczony do analizy wydajności, ale w początkowej fazie projektu programistycznego umożliwia wykrycie nieoptymalnie napisanych funkcji.

Przy wykorzystaniu koncepcji

JUnit powstało wiele komercyjnych aplikacji testujących, które głównie rozszerzają moduły do zarządzania złożonymi zestawami testów i oferują strategie testowania. Dysponując dodatkowymi informacjami o wadze poszczególnych modułów, możliwe jest nawet opracowanie listy "najważniejszych poprawek".

Dzięki tego typu narzędziom generowanie testów jest znacznie mniej pracochłonne. Na przykład narzędzie Jtest firmy Parasoft jest wyposażone w gotowe kreatory, które po wybraniu kilku opcji generują testy - zarówno sprawdzające, czy obiekt w ogóle może być utworzony, jak i badające funkcjonalność kodu i analizujące spójność rozwiązania. Użytkownik może samodzielnie definiować szkielety testów lub zmieniać te, wygenerowane przez Jtest.

Dla technologii .Net jest dostępny darmowy produkt NUnit (funkcjonalnie odpowiadający JUnit i oparty na tych samych założeniach). Wśród narzędzi komercyjnych warto wymienić HarnessIt. Wykorzystuje on możliwości, jakie stwarzają atrybuty w językach .Net. Zamiast tworzyć oddzielne klasy testowe, w klasie są osadzane specjalnie oznaczone funkcje z odpowiednim kodem testowym. Ponadto można określać wymaganą kolejność testów (w JUnit trzeba było to robić z poziomu zewnętrznego programu, w tym przypadku jest to parametr atrybutu). HarnessIt pozwala także na testowanie witryn WWW i zdalne wywoływanie testów.


TOP 200