Antywzorce w testowaniu oprogramowania. Testowanie niewłaściwej funkcjonalności

Każdy, kto ma odrobinę doświadczenia w testowaniu oprogramowania, a zwłaszcza w pisaniu testów automatycznych, wie, jak trudne, czy nawet bliskie niemożliwości, jest uzyskanie stuprocentowego pokrycia kodu testami. W teorii da się „otestować” każdą linijkę kodu, każdą funkcjonalność, każde wejście i wyjście – wszak lista możliwych przypadków jest skończona. Praktyka jednak, poparta potrzebami biznesowymi i priorytetami, sprawia, że zwykle 100% jest Świętym Graalem, a osiągnięcie go jedną z dwunastu prac Herkulesa.

Magdalena Drechsler-Nowak. Software Quality Assurance Engineer w Altium. Na co dzień zajmuje się testowaniem oprogramowania, co sprawia jej niemałą frajdę. Jej droga do testowania nie była wprawdzie długa, ale też niezbyt prosta – możesz o niej przeczytać w pierwszym wpisie na swoim blogu. Lubi swoją pracę do tego stopnia, że gdy zaczyna o niej rozmawiać, to „testowanie” odmienia przez wszystkie przypadki. W wolnych chwilach pochłania ogromne ilości książek, zwłaszcza literaturę faktu.


Najłatwiej osiągnąć ten poziom na „świeżym” projekcie, kiedy zaczynamy od zera. Jeżeli od początku będziemy brali w developmencie testy pod uwagę, skorzystamy z dobrodziejstwa TDD, będziemy za wszelką cenę unikać długu technologicznego, istnieje szansa, że uda się utrzymać pokrycie na poziomie 100%. W teorii brzmi to pięknie. Zwykle natomiast taka projektowa tabula rasa oczekuje rychłego zapisania w postaci ROI (zwrotu z inwestycji) – co powoduje silne parcie na ukończenie aplikacji, a to sprzyja powstawaniu długu technologicznego. Często także najbardziej kuszącym rozwiązaniem jest pominięcie testów.

Odrębną sprawą jest to, że „świeżynki” to tylko jakaś część wszystkich rozwijanych aplikacji (moje doświadczenie podpowiada, że mówimy raczej o małym procencie). Większość z nas nie będzie miało tylko szczęścia, by z wymarzoną częstotliwością zaczynać od zera. Zwykle dziedziczymy projekty po kimś, kto je rozwijał (z – uwaga – wspomnianym długiem technologicznym). Aplikacja taka może mieć (jeśli sprzyja nam fortuna) minimalną liczbę testów, a czasem może nie mieć ich wcale.

Romantyczna (i czasochłonna) idea pisania testów zarówno dla nowego, jak i odziedziczonego kodu może szybko legnąć w gruzach, odrzucona przez kierownika projektu – biznes jest raczej zainteresowany maksymalną koncentracją na dodawaniu nowych funkcjonalności, refaktoryzacja jest raczej rozpatrywana w kategoriach przykrego obowiązku i to też raczej wtedy, kiedy już wisimy nad krawędzią przepaści.

Cóż nam pozostaje? Musimy znaleźć odpowiednią równowagę pomiędzy dodawaniem testów do nowych funkcji i rozszerzeniem istniejącego zestawu testów (to drugie sprawi, że i nowe funkcje z trudem uzyskają magiczne 100% – z czegoś trzeba zrezygnować).

I tu przechodzimy do sedna tematu – w takiej sytuacji (nie tak rzadko występującej, jak już wspomniałam) istnieje ryzyko, że zmarnujemy nasze zasoby – czas i energię – na testowanie czegoś, co nie przyniesie nam dostatecznej wartości.

Załóżmy, że nasza aplikacja to sklep elektroniczny (przykład z Codepipes Blog). W czasie testowania znaleźliśmy dwa błędy:

  • użytkownik nie może sprawdzić zawartości swojego koszyka, co sprawia, że sprzedaż jest niemożliwa,
  • użytkownik otrzymuje złe rekomendacje podczas przeglądania produktów.

Oba błędy powinny zostać naprawione, ale już na pierwszy rzut oka widzimy, że jeden z problemów ma wyższy priorytet. Wychodząc z tego założenia – niejako przewidując pewne problemy – możemy więc wybrać, który element aplikacji jest bardziej odpowiedni do zautomatyzowania (w tym wypadku powinniśmy raczej skupić się na testach koszyka niż systemu rekomendacji).

W skrócie naszą aplikację możemy podzielić na trzy części (model mentalny zaproponowany przez Codepipes):

  • Krytyczny kod – jego wpływ na użytkowanie aplikacji jest największy, w jego zakresie wprowadzane są zmiany najczęściej; ma też największą tendencję do ujawniania usterek
  • Kod podstawowy – czasem się psuje, dostaje kilka nowych funkcji i ma średni wpływ na użytkowników aplikacji
  • Pozostały kod – rzadko jest zmieniany, najrzadziej otrzymuje nowe funkcje i ma minimalny wpływ na użytkowników aplikacji.

Jak więc wybrać część naszej aplikacji, którą powinniśmy pokryć testami? Wystarczy odpowiedzieć na pytanie, czy funkcjonalność, którą obecnie testujemy, należy do kategorii krytycznych lub podstawowych. Jeśli tak, to warto napisać test. Jeśli nie, z czystym sumieniem możemy skupić się na innych zadaniach i wykorzystać zasoby na coś, co przyniesie większą wartość projektowi (niekoniecznie w zakresie automatyzacji czy pisania przypadków testowych).

Próbując osiągnąć jak najwyższy poziom pokrycia testami, powinniśmy raczej robić to stopniowo: najpierw osiągnąć mityczne 100% dla krytycznych funkcjonalności, a dopiero wówczas, gdy to osiągniemy, skupić uwagę na kodzie o niższym stopniu krytyczności. Testowanie funkcjonalności na chybił-trafił lub po prostu „od góry”, nie biorąc pod uwagę żadnej priorytetyzacji, nie ma raczej dużej szansy na przyniesienie korzyści.


Artykuł został pierwotnie opublikowany na jakosctobedzie.pl.

Patronujemy

 
 
Polecamy
Alternatywa dla JMeter, czyli testowanie wydajności z Locust. Cz.2