13 powodów nieudanej automatyzacji testów, część 2
Automatyzacja testów oprogramowania nie polega na wykonaniu testów manualnych przez automat – to semiautomatyzacja.
W poprzedniej części wymienione zostały różne powody, dla których Developerzy w Zespołach wytwarzających oprogramowanie nie zajmują się automatyzacją testów i nie biorą za nią odpowiedzialności. A przecież takie rozwiązanie jest jedynym sensownym: ludzie, którzy budują produkt, powinni zapewnić, że on działa, czyli przetestować go.
Niestety z faktu, że Developerzy będą tego świadomi, wcale nie wynika, że automatyzacja testów jest od tego momentu skazana na sukces. Dziś garść pułapek, w jakie można wpaść, chcąc zrobić automatyzację dobrze.
6. Tylko testy czarnoskrzynkowe są wiarygodne
Developerzy tworzący Zespół mają różne kompetencje: niektórzy znają się lepiej na programowaniu, inni na testowaniu, jeszcze inni na aspektach biznesowych produktu lub użyteczności interfejsów użytkownika. Zespół, który podejmuje się automatyzowania testów, w naturalny sposób zwróci się do doświadczonych testerów manualnych po wskazówki, co i jak powinno być testowane. I tu może wpaść w pułapkę próby testowania wszystkiego na najwyższym możliwym poziomie poskładanych systemów.
Wśród wielu testerów panuje bowiem przekonanie, że testy są wiarygodne wyłącznie wtedy, gdy tworzone i wykonywane są bez jakiejkolwiek wiedzy o implementacji testowanych rozwiązań. Kierowany przez testera o takich poglądach Zespół może zacząć preferować podejście czarnoskrzynkowe do testowania. A ma ono kilka zasadniczych wad.
Po pierwsze, większość testów projektowanych będzie z założeniem, że da się je wykonać, dopiero gdy produkt zostanie w pełni ukończony. I koniecznie wykonywać je trzeba na zintegrowanych, w pełni działających systemach – bo przecież testy jednostkowe lub integracyjne „nie są wiarygodne” wedle tej filozofii. Dlaczego? Bo rzekomo „nie sprawdzają obiektywnie funkcjonalności”.
Po drugie, koszt budowy niezbędnych środowisk i przygotowania danych do takich testów będzie znaczący, podobnie jak sam czas wykonania testów. Im bowiem wyższy poziom, na jakim się testuje, tym trudniej wyizolować obiekt testowy i zapewnić jego stabilność (kontrolę nad stanem testowanego rozwiązania).
Po trzecie, niemożliwe będzie wykrycie słabości przyjętego rozwiązania, ponieważ póki będzie ono działać, nikt nie zajrzy produktowi pod maskę. Inaczej mówiąc, nieważne, jak coś jest napisane, ważne, że działa.
Po czwarte wreszcie, podejście to może spowodować, że zaprojektowanych zostanie mnóstwo testów, które z uporem maniaka przechodzą po wielokroć przez ten sam kawałek kodu. Jeśli nie wiemy, że tak jest (bo nie znamy implementacji), trzeba je wszystkie wykonać, aby mieć pewność, że w każdym przypadku produkt zadziała.
W praktyce dobrze jest pamiętać o piramidzie testów: zastanówmy się, co należy przetestować, po czym poszukajmy sposobu potwierdzenia, że to działa, w najprostszy i najmniej kosztowny sposób. Jeśli to możliwe, to na poziomie pojedynczej klasy w kodzie (czyli w testach jednostkowych). Praktyka pokazuje, że dobre testy automatyczne są prawie zawsze mieszaniną podejścia biało- i szaroskrzynkowego. Natomiast testów czarnoskrzynkowych jest jak na lekarstwo i służą one najczęściej nie weryfikacji, tylko walidacji (potwierdzeniu wartości), albo szybkiemu sprawdzeniu, czy produkt w ogóle działa (czyli są to tzw. smoke tests).
7. Sprawdźmy wszystko przy okazji…
Jest sukces: w końcu testów automatycznych, na różnych poziomach, zaczyna przybywać. Bardzo szybko wychodzi kolejne przyzwyczajenie testerów manualnych, które może przenieść się na Zespół, w którym będą oni liderami automatyzacji. Chodzi o nawyk sprawdzania nie tylko tego, czego dotyczy wykonywany scenariusz testowy, ale też wszystkiego dokoła.
To dobry nawyk, jeśli wykonujemy testy manualnie. Brak automatyzacji ogranicza możliwości sprawdzenia produktu na okoliczność regresji we wcześniej działających funkcjonalnościach i testerzy muszą zwracać niejako przy okazji uwagę na symptomy, że tak się stało.
Jeśli jednak posługujemy się testami automatycznymi, musimy ograniczać zakres sprawdzenia, jakiego dokonuje się w każdym z nich. Nie oznacza to konieczności rezygnacji z poszukiwania regresji. Skoro możemy uruchamiać testy równolegle, szybciej i w sposób powtarzalny, warto unikać upychania zbyt wielu sprawdzeń w każdym z nich. Lepiej stworzyć ich więcej, a potem wykonać wszystkie.
Ułatwi to implementację testów, ale też pozwoli łatwiej zrozumieć, na czym polega błąd, jeśli na jakiś natrafimy. Jeśli w ramach jednego testu sprawdzać będziemy kilkanaście rzeczy, to mimo automatyzacji ręcznie będziemy musieli sprawdzać, która z nich zadziałała źle. Jeśli każdy test będzie weryfikował określoną jedną rzecz, od razu będzie wiadomo, co nie działa – wystarczy sprawdzić, czego dotyczyły testy, które zakończyły się negatywnie.
8. Skoro test się wykonał, to wszystko działa
Gdy już Zespół umie tworzyć testy wykonujące się szybko i powtarzalnie, może okazać się, że wiele z automatycznych testów tak naprawdę… nie jest testami. I bynajmniej nie jest tak, że zawsze dają pozytywne wyniki – jak najbardziej potrafią znaleźć błąd i na nim się zatrzymać, ale diabeł zawsze tkwi w szczegółach.
Gdyby przejrzeć testy dla złożonego produktu w dowolnej większej firmie, bez trudu znajdziemy takie skrypty, w których nie ma żadnej asercji, a więc sprawdzenia, czy testowany produkt działa poprawnie. Twórcy takich scenariuszy opierają je na następującej logice: jeśli scenariusz wykonał się do końca, to znaczy, że produkt zadziałał, a jeśli to się nie udało, bo wystąpił jakiś błąd… a, no to wtedy trzeba będzie się temu przyjrzeć.
Takie podejście oznacza, że mamy do czynienia z semiautomatyzacją: za każdym razem, gdy testy zgłoszą błędy w trakcie wykonania, trzeba będzie ręcznie sprawdzić, co się stało. Brak asercji utrudni zrozumienie, na czym polega problem. Co gorsza, zachodzi tu domniemanie, że scenariusz testowy został zaimplementowany w sposób uniemożliwiający jego pozytywne zakończenie, jeśli produkt działa źle. A to nie zawsze musi być prawdą/
Dlatego krytycznym wymogiem jest zawarcie w każdym z testów przynajmniej jednej asercji, która sprawdza warunek bezwzględnie rozstrzygający, czy test zakończył się pomyślnie, czy też nie.
9. Żeby testować, trzeba znać produkt
Narzędzia i praktyki użyte w procesie testowym na nic się nie przydadzą, jeśli projektujący, implementujący i wykonujący te testy nie rozumieją, jak ma działać produkt. Dotyczy to zarówno testów wykonywanych manualnie, jak i testów automatycznych.
Konsekwencją owego braku wiedzy jest nie tylko brak asertywności – a więc ignorowanie błędów. Równie często testy takie skupione są na rzeczach mało istotnych, prowadzą do uznania poprawnie działającej funkcjonalności za błąd albo testowane są nierealne (nie mogą się w praktyce wydarzyć) scenariusze.
Twórcy testów (cały Zespół, a nie tylko „dedykowani testerzy”) powinni znać produkt na tyle, by skupić swój wysiłek na tym, co istotne i by dopasować poziom testowania do oczekiwanego poziomu jakości produktu.
Równie trudne okaże się automatyzowanie testów dla produktu, w którym wymagania są na tyle niejasne i niestabilne, że nigdy nie udaje się uzyskać wspólnego ich zrozumienia z interesariuszami. Dzieje się to często tam, gdzie złożoność produktu przerosła zdolności jego ogarnięcia przez kogokolwiek w organizacji – być może to dobry moment, by dokonać w nim drastycznych cięć i uproszczeń?
10. Jak, u licha, przetestować coś takiego…?!
Zwyczaje wykształcone w czasach, gdy większość testów Zespół wykonywał manualnie, mogą odbić się mu czkawką nawet wtedy, gdy już nauczył się dość skutecznie automatyzować.
Warto mieć świadomość, że tester manualny może bez trudu zweryfikować funkcjonalności i cechy produktu, których sprawdzenie automatyczne będzie ekstremalnie kosztowne. Człowiek znający wymagania i produkt potrafi być jednocześnie asertywny i tolerancyjny względem testowanego rozwiązania, dokonuje na bieżąco oceny, czy coś działa dobrze, czy źle, potrafi się też posłużyć kategorią akceptowalnego działania produktu…
Automat tego nie potrafi. Jasne, można wbudować parametry zwiększające tolerancję na błędy, ale za cenę utraty pewności, że testujemy to, co chcieliśmy. Efektem może być konieczność ręcznego sprawdzenia rezultatów wszystkich takich testów, nawet tych wykonanych z sukcesem – a to zaprzeczałoby koncepcji przyspieszenia procesu testowego poprzez automatyzację.
Dlatego warto już na etapie projektowania i planowania rozwiązań dyskutować, jak będą one testowane. Nierzadko wymaga to dyskusji z interesariuszami o tym, jak bardzo niektóre z oczekiwanych zmian skomplikują dalsze utrzymanie produktu. A w czasie implementacji zmian konieczne jest zadbanie o to, by dało się je łatwo testować, nawet jeśli wymaga to nieco więcej wysiłku od programistów, gdy piszą kod.
To jeszcze nie koniec!
W kolejnej, trzeciej części przyjrzymy się temu, jak organizacja może Zespołom skutecznie utrudnić automatyzację testów.