#18

Sebastian Chmielewski - O zapewnianiu jakości oprogramowania

W tym odcinku Łukasz Kobyliński rozmawia z Sebastianem Chmielewskim o zapewnianiu jakości oprogramowania. Rozmawiamy o testowaniu manualnym, automatycznym oraz metodach zapewniania jakości oprogramowania.

Streszczenie odcinka

  1. Czym są testy manualne, a czym automatyczne?
  2. Do czego służą testy automatyczne?
  3. Czy testerzy manualni są jeszcze potrzebni?
  4. Czym jest Quality Assurance, czyli zapewnianie jakości oprogramowania?
  5. Jakie są typowe rodzaje testów?
  6. Jak w praktyce tworzy się testy automatyczne?
  7. Jak zostać testerem automatycznym? Czy tester automatyczny musi być programistą?
  8. Jaka jest relacja między testerem a programistą?
  9. Ile testów musimy napisać? Czym jest pokrycie kodu testami?
  10. Czy każdą aplikację da się testować automatycznie?
  11. Jakie są dobre praktyki tworzenia testów automatycznych?
  12. Jak można analizować wyniki testów? Co zawiera raport z testów?
  13. Jakie są typowe błędy przy testowaniu?
  14. Czy można osiągnąć stan, w którym uznamy, że aplikacja jest całkowicie pozbawiona błędów?

Transkrypcja odcinka

Cześć! Z tej strony Łukasz Kobyliński. Witam Was w kolejnym odcinku podcastu „Stacja IT”. Dzisiaj mam przyjemność rozmawiać z Sebastianem Chmielewskim o testowaniu i o zapewnianiu jakości oprogramowania.


Cześć, Sebastian!

Cześć, witam wszystkich!

Czy możesz na wstępie powiedzieć parę słów o sobie: czym się zajmujesz i jak doszło do tego, że robisz to, co robisz?

Na co dzień zajmuję się szeroko pojętym zapewnianiem jakości. Moja ścieżka zaczęła się od tego, że byłem testerem manualnym. Już na studiach miałem praktyki w firmie informatycznej. Trafiłem do nowo powstałego działu związanego z zapewnianiem jakości, quality assurance – QA. Na początku były to testy manualne, czyli manualna weryfikacja działania aplikacji. Pojawiały się też elementy testów automatycznych. Pisaliśmy skrypty weryfikujące jakieś komponenty, smoke testy. Z czasem zacząłem rozwijać się w kierunku automatyzacji i testów wydajnościowych. Obecnie zajmuję się głównie testami bezpieczeństwa. Mój ulubiony cytat na temat testów bezpieczeństwa to ten, że są one bardzo dokładnym testowaniem oprogramowania.

Co to są testy manualne, kto je wykonuje i czy są one robione wg ustalonej kolejności?

Testy manualne wykonywane są ręcznie. Tester przechodzi ręcznie przez aplikację w taki sposób, w jaki robi to końcowy użytkownik. Testy automatyczne polegają najczęściej na zautomatyzowaniu tych samych czynności. Specyficzną odmianą testów są smoke testy. W ich przypadku, podobnie jak i przy prototypowaniu urządzeń, po pierwsze jest odpalenie, czyli podłączenie do prądu i sprawdzenie, czy nie dymi. Odpowiednikiem tego w IT jest uruchomienie systemu i sprawdzenie, czy rzeczywiście się uruchomił, czy coś nie padło. Jest to więc rodzaj prostych skryptów, które weryfikują podstawowe funkcjonalności, działanie podstawowych komponentów, np. przysłowiowy test prezesa, w którym wejście do aplikacji, zalogowanie się na główne konto i otwarcie strony głównej odbyło się właśnie przy użyciu skryptu.

Czy w sytuacji, w której zaczynamy korzystać z testów automatycznych, testerzy manualni nie są już potrzebni, bo możemy wszystko oskryptować, czy jednak nadal w pewnych sytuacjach korzysta się z testów manualnych?

Moim zdaniem celem testów automatycznych nie powinno być zastąpienie testów manualnych. Testy automatyczne mają potwierdzić, że to, co zostało zrobione, działa. Czasami możemy mocno się napracować przy budowie jakiejś aplikacji, zakodować ją poprawnie wg specyfikacji, a potem klient końcowy stwierdzi, że to nie jest to, np. moduł raportowania nie miał tak wyglądać. Dlatego testy automatyczne to dobra praktyka inżynierska, zapewniająca nam powtarzalność testowania: to, że test automatyczny, skrypt będzie testował, wykonywał te same testy, będzie powtarzalny za każdym razem, natomiast nie zastąpi intuicji, wiedzy i zdrowego rozsądku, który mają doświadczeni testerzy manualni, tacy, którzy mają zmysł do psucia, do wyszukiwania problemów, a także wiedzę na temat tego, gdzie i w jaki sposób dana aplikacja będzie używana. Dla przykładu: w dużym banku bardzo ważna jest wiedza na temat tego, jak dana organizacja działa, czego test automatyczny, który jest skryptem, nie będzie wiedział, a tester manualny może lub powinien mieć taką wiedzę.

Czyli jest jakaś kategoria testów, które wykonuje się w sposób automatyczny, a jakieś inne testy wykonuje się w sposób manualny? Wyobrażam sobie, że wszystkiego nie są się oskryptować. Wybiera się pewnie najbardziej typowe scenariusze lub takie krytyczne z punktu widzenia danej aplikacji. Czy są jakieś dobre wzorce mówiące o tym, co zwykle testuje się w sposób automatyczny, a co zostawia testerom manualnym?

Testy automatyczne mają potwierdzać, że dana aplikacja w takim stanie, w jakim została napisana, dalej działa. Są to tzw. greenpathy, czyli pozytywne, proste ścieżki przechodzenia przez tę aplikację, podstawowe scenariusze, te najważniejsze dla użytkowników końcowych. Każda aplikacja ma funkcję logowania i nie chcemy, aby przestała nagle działać po wdrożeniu. To jest naturalny kandydat do automatyzacji. Dlatego testy automatyczne są dobre do testów, które są żmudne, powtarzalne, np. testy na zbiorach danych – mamy Excela, wielką tabelkę i każdy scenariusz różni się zestawem danych, które mamy wpisać w jakiś formularz lub wysłać do usługi i sprawdzić końcowy wynik. Inne testy są długotrwałe czy nawet specjalnie zaprojektowane pod kątem automatyzacji, której wykonanie w sposób ręczny mijałoby się z celem albo byłoby bardzo trudne, np. testy wydajnościowe. Obciążenie aplikacji pięćdziesięcioma, setką równoległych użytkowników w sposób manualny byłoby przedsięwzięciem logistycznie bardzo ciężkim do wykonania, a przy automatyzacji – jak najbardziej wykonalnym. To tylko kilka przypadków, kiedy automatyzacja jest jak najbardziej wskazana.
Ale nie każda aplikacja nadaje się do automatyzacji. Przykładem są tu powiązania z jakimiś zewnętrznymi systemami, z których musimy pobierać dane. Tych danych nie możemy łatwo uzyskać albo nie możemy ich po testach wyczyścić lub usunąć. Niektóre aplikacje, szczególnie te leciwe, napisane w sposób trudny do zautomatyzowania, nie mają np. identyfikatorów elementów i skrypt, który ma w coś kliknąć, nie może łatwo znaleźć tego elementu, w który ma kliknąć. W takich przypadkach testy manualne to jedyna ścieżka.

Wspomniałeś o haśle quality assurance, czyli ogólnie o zapewnianiu jakości oprogramowania. Jak testowanie ma się do zapewniania jakości?

Tradycyjnie rozumiane testowanie to weryfikacja, czy nie ma błędów, czyli czy funkcjonalności działają poprawnie. Dostajemy aplikację, funkcjonalność wykonaną przez programistów i weryfikujemy, czy ona działa. Ale niestety czasami, gdy coś zostało wydevelopowane, oddane na testy, znajdujemy tam poważne błędy albo stwierdzamy, że aplikacja nie miała zostać tak zrobiona, a jest już za późno, aby ją skorygować. Jeśli koszt naprawy byłby znaczny, to w tym wypadku zapewnienie jakości od testowania różni się tym, że jest próbą zapewnienia odpowiedniej jakości na wszystkich etapach budowy oprogramowania, począwszy od zbierania wymagań, programowania zapewnienia jakości także samych testów, czyli testowanie testowania do testów akceptacyjnych, testów wydajności, które tradycyjnie wykonuje się na koniec życia projektu. Natomiast samo testowanie jest tylko wykrywaniem już istniejących błędów. Quality assurance odgrywa rolę prewencyjną. Pilnuje, aby problemy nie wystąpiły. Testowanie to weryfikacja dostarczanego już produktu.

Wspomniałeś o różnych rodzajach testów, np. o testach bezpieczeństwa, akceptacyjnych. Jakie jeszcze rodzaje testów się stosuje i na jakim etapie testowania czy rozwoju oprogramowania?

Aplikacja, którą tworzymy, z punktu widzenia klientów czy użytkowników ma zestaw jakichś cech, który jest dla niej ważny. Podstawowy jest taki, że ona ma działać, czyli robić to, co powinna. Ale dla użytkowników mogą być ważne inne cechy, np. powinna być wydajna, czyli szybko przetwarzać składane w niej zlecenia, być bezpieczna. Aplikacja przeznaczona dla osób niewidomych ma inne wymagania, jeśli chodzi o dostępność, niż ta, które będzie działała w banku i będzie przeznaczona dla pracowników i obsługi ich procesów wewnętrznych. Te różne rodzaje testów odzwierciedlają więc cechy oprogramowania i produktu, które są dla nas ważne.
Testy funkcjonalne to weryfikacja poprawnego działania funkcjonalności przypadków pozytywnych, które najczęściej stosujemy, ale też różnych negatywnych, wprowadzania błędnych danych do wygenerowania bardzo nietypowych przypadków, prób „wywalenia” jakiegoś procesu. Testy wydajnościowe to weryfikacja parametrów wydajnościowych, szybkości przetwarzania, skalowalności. Możliwość przetwarzania różnych wolumenów danych też są ważne w przypadku aplikacji, które mają być superwydajne. Testy wysokiej dostępności przeprowadza się wtedy, gdy nasza aplikacja ma być dostępna 365 dni w roku, przez 7 dni w tygodniu, 24 godziny na dobę. Wtedy wywalamy jakiś komponent, weryfikujemy, czy system jest w stanie obsłużyć taki problem. To są tzw. testy HA. Następnie testy bezpieczeństwa, czyli weryfikacja różnych rzeczy, które nie obejmują innych rodzajów testowania. Gdy mamy błąd i coś nie działa, to jest to błąd funkcjonalny. Gdy czegoś nie można znaleźć, to błąd użyteczności. Działa zbyt wolno – błąd wydajnościowy. A cała reszta to błędy bezpieczeństwa.

Tymi poszczególnymi testami zajmują się różne osoby. W ramach bycia testerem są różne specjalizacje, bo wydaje się, że czymś zupełnie innym jest testowanie funkcjonalności aplikacji, a czym innym – wydajności czy wysokiej dostępności.

To zależy od dwóch czynników. Pierwszy to gdzie testujemy, czyli w jakiej organizacji, dla kogo testujemy aplikację. To zależy też od złożoności samej aplikacji. W małym start-upie może być tak, że tester musi być człowiekiem orkiestrą i zajmować się wszystkim, bo dostaje jakąś funkcjonalność, ale trzeba jeszcze wykonać test automatyczny czy wydajnościowy. Przy bardzo złożonych czy dużych systemach, dużych aplikacjach, w których jest powiązanych bardzo wiele różnych technologii, bo tu jest jakieś GUI webowe, tu jakaś szyna integracyjna. Pojawiają się więc specjalizacje.
Niektóre narzędzia wymagają specjalistycznej wiedzy czy doświadczenia, ponieważ trzeba je poznać. Popularny JMeter wykorzystywany do testów wydajnościowych – popularny, bo darmowy – czy obecnie konkurencyjny dla niego Gatling to narzędzia, które mają wiele funkcji, wiele różnych komponentów, a do tego działają na niskim poziomie protokołu HTTP, gdzie wymagana jest pewna wiedza i doświadczenie na temat tego, jak te aplikacje działają – te specjalizacje pojawiają się tu naturalnie. Szczególnie jeśli od testera wydajnościowego wymaga się, że nie tylko przeprowadzi testy, ale też wskaże, gdzie w naszym systemie są wąskie gardła, zbada, wykona jakieś profilowanie, badanie parametrów wydajnościowych, baz danych, serwera aplikacyjnego, aplikacji. Wtedy wymaga to większej wiedzy i doświadczenia – i te specjalizacje się pojawiają.

Na czym polega pisanie testów automatycznych? W przypadku testów manualnych mamy kolejność działań zapisaną w skrypcie albo w sposób naturalny tester przechodzi kolejne etapy aplikacji, żeby zobaczyć, czy one działają zgodnie z założeniami. Natomiast jak podejść do tego w sposób automatyczny? Jak w praktyce zrobić, aby to działało w pętli w sposób powtarzalny, zapisany przez nas i wykonywany przez maszynę?

Można wyróżnić tu dwa główne podejścia. Jedno – popularne – oparte jest na nagrywaniu, np. nagrywaniu akcji użytkownika w przeglądarce czy też nagrywaniu requestów wykonywanych przez przeglądarkę, aplikację lub innego klienta, które idą w kierunku serwera aplikacyjnego. A drugie to kodowanie kroków testu automatycznego. Każde z tych podejść ma swoje wady i zalety. W przypadku nagrywania problem jest taki, że jeśli w naszej aplikacji coś się zmienia, to niestety bardzo często musimy nagrać te testy jeszcze raz. To nie ma sensu w przypadku często zmieniającej się aplikacji. Jesteśmy np. w start-upie i co dwa tygodnie mamy sprint, dodawana jest jakaś funkcjonalność. Przebudowywane są różne funkcje, bo okazuje się, że np. trzeba coś przebudować, żeby było bardziej dostępne dla użytkowników. Ta zmienność powoduje, że nagrywanie może nie być najlepszym wyborem. Ale w przypadku aplikacji, która już od dłuższego czasu istnieje, działa, a potrzebne są testy regresji, takie, które weryfikują, czy nie pojawiają się błędy w już istniejących, działających funkcjonalnościach, nagrywanie może być bardzo dobrym i sensownym wyborem. A jak mamy na czym nagrywać, to aplikacja tak bardzo nie będzie się już zmieniała. To jest jedno z podejść.
A drugie jest takie, że wychodzimy od identycznego scenariusza jak w przypadku testów manualnych i tworzymy obsługę czy opisujemy te kroki, które automat będzie wykonywał w postaci języka programowania. Wykorzystujemy to szczególnie wtedy, gdy naszej aplikacji jeszcze nie ma, jest dopiero tworzona. Mamy już zestaw jakichś scenariuszy, które opisaliśmy. Wiemy, co ona mniej więcej będzie robić, ale samych funkcjonalności jeszcze nie ma, one sukcesywnie się pojawiają i tym samym pojawiają się kroki naszego scenariusza. To pokazuje w raporcie, jaka część tej aplikacji jest gotowa, bo widać, ile kroków jest zielonych, ile czerwonych.
Nie ma tu magicznych narzędzi, które bez potrzeby poprawiania generują nam gotowe scenariusze. Czy będziemy nagrywać, czy będziemy je kodować, zawsze potrzeba mniej lub więcej czasu na obchodzenie różnych problemów. Jeśli raz już je rozwiążemy, to zwykle można to w jakiś sposób używalny wykorzystać, ale często powtarzam, że 80% czasu to jest rozwiązywanie problemów, więc zasada 80/20 przewija się tu często.

Czy możesz wskazać konkretne narzędzia, które zwykle stosuje się w testach automatycznych?

Narzędzia, z których możemy skorzystać, zależą od budżetu. Ale jeśli chcemy skorzystać z narzędzi typu open source, to głównym – i chyba niemającym specjalnej konkurencji – jest Selenium. Selenium zwykle jest silnikiem jakichś frameworków – ponieważ samo w sobie jest niskopoziomowe, więc pojawiają się frameworki. Frameworki dostępne są w różnych językach. Zasadą jest to, że framework czy ogólnie kod naszych testów automatycznych dobrze pisać w języku, który wszyscy dookoła znają – np. w Javie, jeśli w naszej firmie kodujemy głównie w Javie. Ktoś może wtedy pomóc nam z jakimś problemem w kodowaniu testów. Łatwiej nam przekonać developerów, programistów do używania tych testów automatycznych, ale też rozwijania ich, aby nie było tak, że to tylko tester dokonuje automatyzacji testów. Jeśli powstaje jakaś funkcjonalność, to jako część tej funkcjonalności dawany jest kod aplikacji, ale też tworzony jest test automatyczny przez developera, który tę funkcjonalność wykonuje albo modyfikuje. To dobry wzorzec i praktyka, sprawdzona w boju, dająca bardzo dobre wyniki. Testy i ich automatyzacja to po prostu praktyka inżynierska, mająca zapobiec występowaniu pewnego rodzaju problemów. Więc angażowanie zespołów w automatyzację jest jak najbardziej dobrym pomysłem. Czyli nie tylko JUnity jako testy kodu, ale jeśli jest taka potrzeba i mamy w projekcie testy automatyczne, to także automatyzacja. Często okazuje się, że jeśli zaangażujemy developerów w automatyzację, to pewne rzeczy, których wcześniej nie dało się zrobić w aplikacji, np. dodać jakiegoś użytecznego feature’a z punktu widzenia automatyzacji, nagle daje się dodać. Ktoś, kto widzi ten problem przy automatyzacji, bo nie może wykonać skryptu, a jest programistą, ma mniejsze opory przed zrobieniem jakiejś małej zmiany.
Bardzo ważną cechą aplikacji jest tzw. testowalność. Nie każdą aplikację da się zautomatyzować, jeśli nie jest stworzona w sposób poddający się testowaniu. Widziałem aplikację, w której był bardzo rozbudowany model uprawnień. Było sobie menu i w zależności od tego, jakie uprawnienia miał dany użytkownik, to w tym menu pojawiały się odpowiednie pozycje. I to menu było ładowane AJAXem, czyli asynchronicznie, więc nie od razu wszystkie pozycje były widoczne, dopóki menu się nie załadowało. Jeśli model wyliczania tych uprawnień był skomplikowany, to były w nim błędy, więc przy automatyzacji pojawiał się problem polegający na tym, że test automatyczny, skrypt chciał coś kliknąć i nie znalazł swojej opcji, którą chciał wykonać, więc przy analizie nie wiadomo, czy on za szybko kliknął, zanim menu się załadowało, czy rzeczywiście wykryliśmy błąd przy ładowaniu uprawnień. Więc to, co zrobili programiści, to dodali flagę, np. dostępną z poziomu JavaScriptu, którą z poziomu testu automatycznego można było zweryfikować. Flaga mówi, że menu zostało załadowane kompletnie, co oznacza, że jak nie znalazłem swojej opcji, którą chciałem kliknąć, to nie mam do niej uprawnień. Znakomicie upraszczało to potem analizę takiego raportu. Z testów już wiemy, dlaczego tej opcji, którą chcieliśmy wywołać, nie ma. Więc to jest przykład, jak zwiększyć, poprawić testowalność aplikacji, aby łatwiej poddawała się automatyzacji.

W zasadzie cały czas mówimy w domyśle o aplikacjach z interfejsem webowym. Czy to oznacza, że inne aplikacje nie powstają? Czy narzędzia, o których wspomniałeś, np. Selenium, służą tylko do aplikacji webowych, czy aplikacje desktopowe też w sposób automatyczny możemy testować jeszcze innymi narzędziami?

Aplikacje webowe to aplikacje wykonywane przez przeglądarkę – Selenium jest tu najbardziej znane. Ale też w aplikacjach, jeśli korzystają chociażby z REST-a, są protokoły komunikacji. Szczególnie w przypadku REST-a znacznie łatwiej się to automatyzuje. Istnieją bardzo fajne i wygodne narzędzia do automatyzacji jak REST-assured. Jeśli chodzi o aplikacje desktopowe, to jest tu o wiele łatwiej, ale mało miałem z tym doświadczeń. Te narzędzia mogą się różnić w zależności od systemu operacyjnego. Interfejsy pod Windowsem są różne w porównaniu z tymi pod Linuksem czy macOS-em – zakładam, że narzędzia są różne, ja mam z nimi mało doświadczenia.

Wspomniałeś o tym, że jeśli testy nie są nagrywane, to są opisane – scenariusz takiego testu zapisany jest w postaci języka programowania. W takim razie jaka jest relacja między testerem a programistą? Czy tester ma być programistą, skoro ma pisać kawałki kodu, które mówią o tym, jak test ma być wykonywany? Czy to jest tak, że w zasadzie musimy mieć bardzo podobne kompetencje, będąc jednocześnie i testerem, i programistą? Jaka jest droga do tego, aby stać się testerem automatycznym i móc te zadania w pełni wykonywać?

Do pisania testów automatycznych wystarcza znajomość języka programowania, który, jak wspomniałem, ma różne frameworki w różnych językach programowania. Ta znajomość nie musi być duża, wystarczy ta na poziomie podstawowym, czyli umiejętność pisania prostych programów. Frameworki mają dosyć ograniczony zestaw API czy metod, które należy wywołać. W przypadku współczesnych aplikacji na pewno przydaje się znajomość JavaScriptu. Natomiast znajomość Javy, .Neta czy też Pythona na poziomie podstawowym jest wystarczająca, by zacząć automatyzować. Problem pojawia się wtedy, kiedy mamy bardzo duży zestaw testów automatycznych, co wiąże się na pewno z jakąś bardzo dużą aplikacją. Wtedy już dobre wzorce, pisanie kodu utrzymywalnego, reużywalnego, wydzielanie metod, które się powtarzają, do bibliotek, jak najbardziej się tu przydaje. Ale tej umiejętności również można nabyć z czasem.

Jak do zagadnienia testowania ma się technika rozwoju oprogramowania TDD, czyli test-driven development? Czy można uznać to za element całego ekosystemu zapewniania jakości oprogramowania, czy jest to rozumiane jako jeden z rodzajów testów, które są implementowane już na wczesnym etapie?

Tak, jak najbardziej. Nawet o tym wspomniałem, więc może fajnie by było, gdyby programista – oprócz tych swoich JUnitów, czyli kawałków kodu, które weryfikują działanie kodu – angażował się też w pisanie testów automatycznych. Z drugiej strony, jeśli mówimy o tym QA, jednym z mierników jakości oprogramowania jest chociażby pokrycie kodu testami, co można mierzyć przy pomocy narzędzi. My np. używamy platformy Sonar, która obsługuje i projekty javowe, i .Neta, i kilka innych języków, gdzie mierzymy tzw. code covery w pokrycie kodu testami jednostkowymi. Można też mierzyć pokrycie kodu w trakcie testów automatycznych, co jest troszkę trudniejsze, ale jak najbardziej wykonalne – pokrycie kodu musi być zintegrowane z aplikacją, którą uruchamiamy w trakcie testów automatycznych. I to jest właśnie jeden z mierników dobrego oprogramowania, czyli ile tego kodu udało nam się pokryć na wyższym poziomie testami jednostkowymi i automatycznymi.

W jaki sposób mierzy się to pokrycie kodu testami? Przy testach automatycznych wydaje się to nieoczywiste.

Pokrycie kodu jest to ilość linii kodu, takich, które rzeczywiście coś wykonują, bo w pliku źródłowym możemy mieć komentarze, nagłówki, deklaracje, które w nim się znajdują, ale niekoniecznie przekładają się na coś, co się wykonuje. Zmierzenie tego kodu przekłada się na wykonywalny kod i obliczenie stosunku tego, co znajduje się w kodzie aplikacji, versus to, co zostało w trakcie tych testów wykonane. Są różne rodzaje tego pokrycia – pokrycie decyzji, warunków czy samych linii. Natomiast te frameworki, z których można skorzystać, są wbudowane chociażby w tego Sonara, o którym mówiłem. Działa to na takiej zasadzie, że do kodu aplikacji dodawane są pewne instrukcje, które potem przy wykonaniu po prostu zaznaczają, że dana linia kodu została wykonana. Po wykonaniu testu narzędzie to zbiera i wie, że dana linijka została wykonana. Na tej podstawie generowany jest raport na zasadzie: zielona linia – wykonana, czerwona linia – niewykonana. Uzyskujemy wtedy stosunek ilość linii kodów wykonanych versus całkowita ilość kodów aplikacji.
Jakie to pokrycie powinno być? Nie ma tu jednej prostej odpowiedzi, ponieważ wszyscy jesteśmy z natury leniwi, więc czasami pisze się testy jednostkowe czy automatyczne, które są proste i testują jakieś nieskomplikowane fragmenty aplikacji, a potem brakuje nam siły i chęci na to, żeby przetestować bardziej złożone jej części. Więc ogólnie przyjęta liczba to 80%. Czasami lepiej mieć 50% kodu aplikacji, ale zweryfikować te trudne algorytmy, niż iść w kierunku tego, że musi to być 80% całego kodu, bo możemy to czasami oszukać, robiąc testy na proste klasy, które nic nie wykonują, a łatwo je wywołać testami jednostkowymi i podnieść wskaźnik pokrycia w sposób trochę sztuczny, oszukany.

Jakie są dobre praktyki w testowaniu, w szczególności w testowaniu automatycznym? Wspomniałeś o testowalności aplikacji. Nie każda aplikacja łatwo daje się testować. Wspomniałeś o sytuacji z menu AJAXowym, które pojawia się z opóźnieniem. Czy każdą aplikację da się zautomatyzować, jeśli jest odpowiednio przygotowana, czy zdarzają się trudne przypadki, jak np. takie bogate aplikacje JavaScriptowe, AJAXowe, w których wszystko się dynamicznie zmienia i są bogate graficznie?

To trudne zagadnienie, ponieważ są różne rodzaje technologii, które były lub są dosyć oporne na to testowanie czy ogólnie na automatyzację, chociażby popularne kiedyś aplety flashowe. Trend, który obecnie mamy – popularność REST-a, czyli jest jakiś REST-owy back-end plus JavaScriptowy front-end – wiąże się z tym, że takie rozwiązania łatwiej testować. Mamy tu z jednej strony jakieś API back-endu, więc nie musimy klikać w przeglądarce, to wchodzi na jakiś formularz, aby na nim coś wywołać, i dopiero to coś generuje akcję po stronie back-endu. Możemy to API bezpośrednio wywołać. To jest jeden wzorzec, czyli aplikacja, która udostępnia interfejsy przeznaczone dla maszyny – bo REST API produkującej jakiś tam JSON czy XML – to jest format dokumentu przeznaczony do łatwego parsowania przez maszynę. Takie aplikacje się łatwiej testuje.
Trudno testuje się aplikacje, które są wielkim monolitem, nie mają interfejsów. Inny problem jest z aplikacjami, które integrują się z innymi aplikacjami, a my nie zawsze mamy te inne aplikacje albo nie mamy nad nimi kontroli, czyli nie da się zbudować tzw. zaślepek, w których moglibyśmy przygotowywać chociażby dane testowe. Więc taka jest możliwość budowania zaślepek do tworzenia danych testowych. To też się przydaje. Tak naprawdę widać dosyć wyraźnie, że sama automatyzacja nie jest ani problemem, ani odpowiedzią na problemy, ponieważ to wszystko wiąże się z tym, jak wygląda ogólnie cała nasza aplikacja. Dlatego też mówiłem trochę o quality assurance. Staramy się zapewniać jakość od momentu zbierania wymagań przez development, dobre praktyki developerskie, wykrywanie kodu, który jest napisany np. w sposób bardzo skomplikowany, co też da się zmierzyć jakimiś narzędziami do tej automatyzacji.

Pozostając przy testowalności: łatwo jest wtedy, kiedy mamy coś bliskiego takiej klasycznej stronie HTML-owej ze znacznikami, które możemy zidentyfikować, sprawdzić ich cechy i pewnie jeszcze do tego te kwestie czasowe, tzn. pojawianie się pewnych elementów czy jakichś komunikacji, są w miarę liniowe i przewidywalne.

Bardziej chodzi o to, że ona zachowuje się w sposób przewidywalny, możemy kontrolować różne aspekty jej działania, np. te dane testowe. Jeśli nasz test automatyczny może te dane testowe przygotować, po teście bez potrzeby dodatkowej interwencji je usunąć, czyli posprzątać po sobie, to łatwo się to automatyzuje. Jeśli w aplikacji trzeba poprosić kogoś, aby nam dane przygotował, ten ktoś musi zadzwonić do kogoś, kto musi odezwać się do kogoś w centrali, która jest w innym kraju, to jest to słaby przypadek do automatyzowania.

Czy są jakieś dobre praktyki dotyczące przygotowywania testów automatycznych? Bo można sobie wyobrazić sytuację, w której przygotowujemy wiele krótkich scenariuszy, np. sprawdzamy, czy dany przycisk istnieje, czy wypełnienie pól login i password oraz naciśnięcie przycisku spowoduje jakąś akcję. Możemy wyobrazić sobie scenariusz długotrwały typu pełna ścieżka korzystania z aplikacji przez użytkownika, która trwa pięć minut. Jak dzieli się testy na poszczególne elementy? Czy opracowane są tu zalecenia, czy to zależy od kontekstu danej aplikacji?

Nie ma jednej odpowiedzi na to pytanie. Są różne podejścia. Podstawowe to te, że testujemy ścieżki pozytywne naszej aplikacji. Mogą to być testy poszczególnych funkcjonalności czy, nazwijmy to, formularzy. Bardzo przydatne są testy end-to-end, czyli takie, które przechodzą przez większą lub budowaną ścieżkę, podążając w sposób, w jaki końcowy użytkownik będzie przechodził przez aplikację. Albo testują jakąś ścieżkę w systemie, który służył do obsługi kredytów hipotecznych. To była cała ścieżka od złożenia wniosku przez jego przeprocesowanie, przez różne oddziały w banku, różnego rodzaju pracowników, oddziały, wśród których jedne zajmowały się analizą, drugie wydrukowaniem i uruchomieniem takiego kredytu, potem obsługą posprzedażową itd.
To, jakiego rodzaju testów potrzebujemy, zależy od tego, jaką aplikację przygotowujemy. To, jakiego rodzaju mamy testy, wpływa na to, w jaki sposób będziemy je odpalać. Testy, które są szybkie, odpalamy przy każdym buildzie w aplikacji naszym continuous integrations. Testy, które są długie, będą odpalane rzadziej, np. w nocy. Raport dostaniemy dopiero rano. Testy krótkie mogą być odpalane po każdej zmianie, po każdym commicie, żeby wiedzieć, czy coś się przypadkiem nie zepsuło. Te, które są dłuższe, zajmują więcej czasu, więc liczymy się z tym, że wynik z ich wykonania dostajemy trochę później, ale one też są ważne, bo w końcu testują całą złożoną ścieżkę.

Mówisz, że z testów generowane są raporty, które są albo po każdym zbudowaniu aplikacji, albo co jakiś czas. Co później z tą informacją robimy i jaką informację dostajemy w tym raporcie? Co składa się na raport i co możemy z tą informacją dalej zrobić, żeby naprawić poszczególne błędy?

To zależy, z jakiego narzędzia czy frameworku korzystamy. Dobrze, gdy raport z testów automatycznych jest powtarzalny, czyli każde uruchomienie nie produkuje nam różnych wyników. Dobrze, gdy jest on czytelny. Więc ewentualne screenshoty, które będą się tam pojawiały, to dobra wskazówka na temat tego, co się wywaliło – czy to będzie screenshot, czy treść requestu, który został wybrany, odesłany z aplikacji. Inny jest raport z testów funkcjonalnych, a inny z testów wydajnościowych. Mówimy tu jednak o identyfikowaniu różnego rodzaju informacji.

Wiedząc, który test nie przeszedł, jak rozumiem, po stronie testera leży to, żeby zidentyfikować, w którym miejscu mniej więcej leży problem, przekazać tę informację do developerów?

Tak naprawdę żaden raport nie będzie idealny i nie stwierdzi nam dokładnie, gdzie jest błąd. To jest dosyć ważna część analizy. Część frameworków u mnie ma jakąś integrację chociażby z JIRą i samo np. stworzy zgłoszenia z opisem błędów. Ale jakiś element weryfikacji zawsze potem jest potrzebny. Im mamy lepszą aplikację, mamy w niej „traceowalność” requestów czy inne funkcje wspomagające analizę, tym jest łatwiej. Więc to jest część jakości, dzięki której możemy potem stwierdzić, co nam się wywaliło, jakiś tam trace czy message-ID, który dodawany jest do requestu i potem – szczególnie w rozproszonych aplikacjach – można ustalić, dokąd nasz request trafił, gdzie został przeprocesowany i gdzie błąd wystąpił w systemie.

Jakie są typowe błędy czy antywzorce związane z tworzeniem testów automatycznych? Na co warto zwrócić uwagę, żeby tego nie realizować w swojej implementacji testu?

Błędów może być dużo, wśród nich jest testowanie rzeczy, których może nie warto testować, takich bardzo trywialnych. Ogólnie rzeczy, które da się przetestować na testach jednostkowych, niekoniecznie trzeba powtarzać w testach automatycznych, szczególnie takich, które są długotrwałe i kosztowne. Gdy testy są duże i skomplikowane, to dobre praktyki programistyczne przydają się, żeby podzielić je na moduły, wydzielić wspólne elementy, a nie robić kopiuj-wklej tego samego kawałka kodu, który nam sprawdza, czy dany element istnieje na stronie. Lepiej stworzyć z tego metodę i sprawdzić, czy istnieje, i z tej metody korzystać. To samo dotyczy kroków testowych. Moduł logowania pewnie będzie potrzebny w każdym scenariuszu, więc musi być wyciągnięty do innego elementu. Z drugiej strony testy nie powinny być za duże.

Jakie narzędzia stosuje się w procesie testowania automatycznego? Wspomniałeś o Selenium, które jest niskopoziomowe, więc możemy albo bezpośrednio z niego skorzystać, albo z jakiegoś frameworku. Wspomniałeś też o narzędziu do raportowania. Czy są tu jeszcze inne elementy, o których powinniśmy wspomnieć, i co by to było?

Jeśli chodzi o zapewnienie jakości kodu, to wspomniałem o Sonarze, który integruje nam analizę statyczną, czyli wyszukiwanie błędów w kodzie na podstawie pewnych detektorów będących w stanie wykryć błędy programistyczne, błędy wpływające na działanie kodu czy też błędy bezpieczeństwa. Jest sporo frameworków, które integrują kilka takich narzędzi. Wśród nich jest Serenity, który pozwala na automatyzację z użyciem Selenium oraz REST Assured, czyli mamy testy GUI-owe przez przeglądarkę plus testowanie jakiegoś REST-a, JMeter, jeśli chodzi o testy wydajnościowe. Testy usług to chociażby SoapUI. Dosyć popularny test Community. To takie główne narzędzia, jeśli chodzi o testowanie. Oprócz tego jest cała masa mniejszych narzędzi. Bo tak naprawdę do testowania możemy wykorzystać wszystko. W wielu przypadkach wystarczy nam przeglądarka, wbudowany inspektor – możemy podglądać elementy, requesty, narzędzia z linii komend, jak np. curl, wget, i ogólnie wszystko, co mamy pod ręką, co w tych zadaniach testowych może się odnaleźć.

Czyli tylko nasza kreatywność jako testera ogranicza nas w tym, co możemy wykorzystać, żeby znaleźć błędy, zidentyfikować, gdzie one mogą się znajdować.

Ludzie z prawdziwie testerskim podejściem do psucia wymyślą nam taki przypadek testowy, że nie potrzebują do tego żadnych narzędzi. Więc testy manualne w dalszym ciągu są potrzebne. Ale jeśli chodzi o automatyzację, to te narzędzia, o których wspominałem, są w dalszym ciągu najbardziej popularne. Są pewne narzędzia komercyjne, których niestety cena bywa mocno zaporowa. Potem okazuje się, że te same czy podobne problemy i tak trzeba w jakiś sposób w tych narzędziach obchodzić. Pamiętajmy też o tym, że jeśli mamy kiepską aplikację, to nie ma magicznych narzędzi, które nam ją zautomatyzują albo poprawią w jakiś sposób jakość.

Na koniec powiedz, czy jest jakaś sytuacja, w której możemy stwierdzić, że jesteśmy bezpieczni, jeśli chodzi o wytwarzanie oprogramowania pozbawionego błędów. Czy można jakoś oszacować liczbę potrzebnych testów czy testerów, która da nam poziom zaufania, że uznamy, że nic złego nas nie może spotkać?

Na pewno nigdy nie można się zatrzymać, ponieważ zapewnianie jakości to nie jest coś, co można raz wykonać. To proces, który trzeba stale usprawniać, mierzyć i cały czas nad nim pracować. Nie jest tu kwestią ilość, tylko to, co z tego procesu wyciągamy i jakie usprawnienia realizujemy. Widziałem różnego rodzaju projekty. W jednych było 40 testerów, którzy na hurra wykonywali testy aplikacji. W innych w niewiele mniejszej złożoności wystarczało kilku testerów czy nawet 2–3 osoby, żeby z powodzeniem taką aplikację zweryfikować. Problemem nie jest liczba ludzi, tylko to, z czym tak naprawdę mamy do czynienia. Są aplikacje proste, są aplikacje złożone. W przypadku złożonych procesów, interakcji i wiedzy biznesowej te liczby mogą się zmieniać.
Testowanie jest bardzo ciekawym, złożonym zagadnieniem. Jeśli ktoś będzie chciał rozwijać się i uczyć, to jest tu naprawdę duże pole do popisu. Jest dużo obszarów, w których można się specjalizować. Testując, poznajemy nasze aplikacje. Nie kończymy tylko na testach funkcjonalnych, lecz idziemy w stronę automatyzacji, testów wydajnościowych. Dzięki temu możemy dowiedzieć się o naszej aplikacji takich rzeczy, o których nie mieliśmy pojęcia.

Sebastian, dzięki za to spojrzenie z lotu ptaka na zagadnienia z obszaru testowania i quality assurance. Temat ten warto kontynuować i przyjrzeć się bardziej złożonym problemom, które pojawiają się w testowaniu kompletnych technologii. Dzięki i do usłyszenia następnym razem!

Ja również dziękuję.

Komentarze

Możliwość komentowania została wyłączona.