Automatyzacja testów – wyjście ze strefy komfortu

Automatyzacja testów – wyjście ze strefy komfortu
Automatyzacja testów to nie tylko pisanie skryptów – to również aktywny udział w procesie zapewniania jakości, kontrolowanie czasu wykonania testów oraz wspieranie innych członków zespołu. Czy jesteście gotowi na wyjście ze swojej strefy komfortu, by dowiedzieć się, w jaki sposób automatyzacja testów może poprawić jakość Waszego oprogramowania i przyspieszyć proces jego wytwarzania?
testerzy+

1. Wprowadzenie

Wytwarzanie oprogramowania (zarówno testowanie, jak i programowanie) zmieniło się w ciągu ostatnich 10 - 15 lat. Wobec tego sposób pracy testera oprogramowania też się zmienił i powinniśmy się z tym pogodzić. Stanowiska osób związanych z testowaniem oraz jakością oprogramowania przyjmują różne nazwy: Software Developer in Test, Test Engineer, Automation Test Engineer itd. Tego rodzaju nazwa sugeruje, że oprócz podstawowych umiejętności testerskich, takich jak projektowanie przypadków testowych lub wykonywanie manualne tych przypadków, wymagane są również techniczne umiejętności. Automatyzacja testów to proces i element zapewnienia jakości oprogramowania.

Artykuł powstał na bazie własnych obserwacji, doświadczenia oraz rozmów z różnymi osobami, które pracują w IT.

2. Dużo pracy, brak czasu na rozwój – czy to prawda?

Osoby, które mają 10 lub więcej lat doświadczenia w testach, często zaczynały swoją karierę jako testerzy manualni. Już wtedy mówiło się o automatyzacji, a prawie każdy chciał, aby firma umożliwiła jej naukę w ramach pracy.

Sporo organizacji zauważyło potencjał oraz korzyści płynące z automatyzacji testowania i poszło tą drogą. Jak to często bywa, nieraz się to udało, nieraz nie. Bywały też sytuacje, że firmy zmuszały do automatyzacji na siłę, nie posiadając wiedzy na temat jakiegokolwiek procesu ani strategii. Ale były też organizacje (i nadal są), którym bardzo zależało, aby wprowadzić automatyzację testów. Niestety, nie zawsze to działało. Wobec tego warto zadać pytanie, co zawiodło? Skoro firma daje przestrzeń do rozwoju, wszyscy mówimy, że chcemy automatyzować i chcemy się uczyć, to gdzie leży problem? Odpowiedzi może być wiele:

  • "No wiesz, spoko. Jak bym chciał/chciała, ale mam tyle klikania, że nie wyrobię."
  • "Czekaj, może w następnym tygodniu, bo mam sporo na głowie."
  • "Ale to się nie opłaca, napisanie jednego testu zajmuje tydzień czasu."
  • ...
  • "Niby mamy te testy, ale zawsze rano część z nich nie działa i to jest bez sensu."

Czy to wszystko na pewno jest prawdą? Czy może brakuje nam zrozumienia dla automatyzacji? A może nie chcemy wyjść ze swojej strefy komfortu i postarać się bardziej?

Dobrze przemyślane testy automatyczne mogą sporo ułatwić, chociaż by przez to, że w miarę szybko dają odpowiedź, czy nastąpiła regresja oprogramowania. Coraz częściej pracujemy w sposób zwinny (np. agile), co implikuje zastosowanie automatyzacji. Ciągłe wymówki, że się nie da i nie mamy czasu, mogą doprowadzić do konfliktów w zespole, co na ogół nie służy dobrej atmosferze.

3. Wyjście ze strefy komfortu

Osoby, przed którymi postawiono szansę albo warunek wejścia w automatyzację testów, mogą zacząć się obawiać, że sobie nie poradzą. Początki kodowania nie są proste dla nikogo, zarówno dla programistów, jak i testerów. Przełożeni również powinni zdawać sobie sprawę z tego, że sporo się zmieni, jeśli „pójdziemy w automatyzację”. Ze strony przełożonych oraz ze strony firmy powinno pojawić się wsparcie automatyzacji testowania, w przeciwnym wypadku będzie ona skazana na niepowodzenie. Wymaga to zgłębienia wiedzy na temat procesów, strategii, dobrych praktyk kodowania. Można to osiągnąć poprzez szkolenia pracowników, program wymiany wiedzy wewnątrz firmy, prezentowanie na szerszym forum tego, co udało się osiągnąć oraz dyskusja po takiej prezentacji.

Automatyzacja to nie tylko “składanie testu z klocków”, które daje nam framework. Sam framework będzie wymagał zmian wraz ze zmianami oprogramowania, a to już programowanie i nikt nie powiedział, że tym ma się zająć tylko programista. Jako testerzy nie powinniśmy od tego uciekać. Nie możemy też uciekać od kontroli wykonania naszych testów czy tematów devopsowych, CI (np. Jenkins, TeamCity, tworzenie pipeline’ów w postaci plików), strategii tworzenia gałęzi rozwojowych oraz systemów kontroli wersji (gitflow, github itd.) Wszystkie wymienione elementy łączą się w całość. Musimy zrozumieć zależność tych elementów, o ile chcemy zacząć automatyzować testy na poważnie.

Automatyzacja testów, podobnie jak testowanie manualne, powinna zacząć się od analizy. Projektowanie przypadków testowych, które chcemy zautomatyzować, jest analogiczne jak projektowanie testów manualnych. Techniki są takie same w obu przypadkach. Testy automatyczne mogą występować na różnych poziomach i wobec tego należy rozważyć, co i na jakim poziomie chcemy automatyzować. W literaturze występuje najczęściej taki podział testów funkcjonalnych. Idąc od początku:

  • testy jednostkowe
  • testy integracyjne (małe lub duże)
  • testy systemowe
  • testy akceptacyjne

Gdzieś wśród tego podziału często występują jeszcze testy kontraktowe (służące do sprawdzenia kompatybilności interfejsów), które również należy wziąć pod uwagę. 

Jeżeli mówimy o automatyzacji testowania, musimy mieć na uwadze poziomy testów, ponieważ nie wszystko trzeba automatyzować na poziomie testów akceptacyjnych. Większość funkcjonalności na ogół można sprawdzać niżej – przesunąć testy w lewo. Dzięki temu są one szybkie, nie wymagają stawiania bazy danych i możemy stosunkowo dużo takich testów napisać w krótkim czasie. Nie jest prawdą (co często możemy spotkać w literaturze), że testy integracyjne powinien pisać programista. Istnieją firmy, w których robią to testerzy, a taka strategia działa bardzo sprawnie. To bardziej kwestia dogadania się z programistą, niż przyjęte zasady. Analizę oraz projektowanie przypadków można zrobić wspólnie oraz zdecydować, co da się zaimplementować na niższym poziomie. Oczywiście, wymaga to umiejętności programowania oraz analizy kodu. Kultura pracy w IT jest na ogół taka, że pracujemy drużynowo i zawsze można liczyć na wsparcie, wystarczą tylko chęci. Początki nie są łatwe, ale z czasem będzie tylko lepiej. Jeśli pracujemy w zespole, to razem jesteśmy odpowiedzialni za produkt. Osoby bardziej doświadczone powinny wspierać pozostałych członków zespołu oraz zachęcać do podnoszenia swoich umiejętności.

4. Rola testera automatyzującego w organizacji

Jeżeli będziemy chcieli odpowiedzieć na pytanie, jaka jest rola testera, który zajmuje się automatyzacją oraz jakością oprogramowania, to taka odpowiedź nie będzie prosta. Takie pytanie często pada na rozmowach o pracę i niestety odpowiedź nie zawsze jest satysfakcjonująca. Sporo osób automatyzujących testy nie widzi swojej roli jako części procesu zapewnienia jakości oprogramowania. 
Jak już wspomnieliśmy w poprzednim rozdziale, mamy różne poziomy testów. Załóżmy, że powstała nowa funkcjonalność i chcemy mieć do niej automatyczne testy. Jak do tego podejść, żeby uwzględnić dobre praktyki, np. zasadę FIRST oraz poziomy testowania? Wytwarzanie oprogramowania to analiza, pisanie kodu oraz testy. Wobec tego można zaplanować spotkanie w składzie: analityk, tester, programista – three amigos. Taki tymczasowy zespół będzie miał o wiele szerszy punkt widzenia niż pojedyncza osoba. Analityk zwróci uwagę na funkcjonalności, tester zaproponuje testy, a programista powie, na jakich poziomach będziemy mogli je zaimplementować. 

Jednak jako testerzy musimy być pewni, że przypadki testowe pokrywają wszystko, co potrzeba.  Programista z kolei będzie wymagał, żeby kod testów był czytelny i napisany zgodnie z dobrymi praktykami kodowania. Taka para może wiele wnieść, ale wymaga od testera umiejętności programowania albo umiejętności wykonania przeglądu kodu – ktoś musi sprawdzić testy napisane przez programistę. Jako testerzy automatyzujący również musimy znać dobre praktyki kodowania. To już nie tylko pisanie testów, ale również branie udziału w procesie zapewnienia jego jakości. Rolą testera w przypadku rozległej automatyzacji, będzie nie tylko analiza wymagań oraz projektowanie przypadków testowych, ale będzie to dodatkowo:

  • pomaganie programistom w analizie pokrycia wymagań lub po prostu implementacja testów integracyjnych czy e2e
  • weryfikowanie testów pisanych przez programistów
  • dbanie o proces testowy
  • kontrola czasu wykonania testów
  • kontrolowanie, jak zachowują się pipeline`y
  • proponowanie usprawnień, o ile są możliwe. 

Testerzy automatyzujący powinni również brać udział w przeglądach kodu oraz zwracać uwagę na testowalność kodu pisanego przez programistów, zwłaszcza na możliwość łatwego mockowania. Umiejętność mockowania jest niezbędna, jeśli jako testerzy chcemy pisać testy na niższym poziomie. Kolejne wyjście ze strefy komfortu: “Weź aktywny udział w code review, naucz się mockować, weź aktywny udział w doskonaleniu procesu testowego”.

W całej automatyzacji bardzo ważny jest jeden fakt: “Poprawnie zaimplementowane testy automatyczne oraz dobrze przemyślana strategia automatyzacji będą zapewniały podatność aplikacji na zmiany”. Mając rozległą automatyzację, przemyślane testy oraz dobrze zaprojektowaną ciągłą integrację (CI) od razu wiemy, czy nie wprowadziliśmy regresji. Być może nie będzie potrzeby zgłaszania defektów (za często), ponieważ zrobi to nasz CI. W dzisiejszych czasach możemy blokować łączenie (merge) rozwojowych gałęzi (branchy), jeśli testy nie przejdą. Duża liczba testów na niższych poziomach da nam taką odpowiedź stosunkowo szybko. Można to zrobić na każdym pull request-cie albo nawet po każdym wgranym kodzie (commicie), co oszczędza czas pracy oraz koszty projektu. 

5. Długi czas implementacji oraz wykonania testów automatycznych

Wiele organizacji boryka się z tym problemem, że czas implementacji jednego testu jest długi. Dodatkowo całość wykonania testów trwa tak długo, że można to zrobić jedynie w nocy – co często prowadzi do trudnych wydarzeń. Testy automatyczne powinny w stosunkowo krótkim czasie wygenerować informacje o jakości. Może dochodzić do sytuacji, gdy po uruchomieniu nie zwracamy uwagi na wyniki testów, które zakończyły się niepowodzeniem, choć funkcjonalność działa. Wiadome jest, że powinniśmy poprawić testy, ale jak pokazaliśmy na początku, jest to długi proces. Słyszymy wtedy odpowiedź: “Kiedyś się je poprawi, teraz nie ma na to czasu…”, co jest proszeniem się o kłopoty.

Pojawiają się pytania: Czy testy są dobrze napisane?”, „Czy testy coś wnoszą, ponieważ działają jaka chcą?”, “Czy nasz proces jest poprawny?”. 

5.1 Długi czas implementacji testów automatycznych

“Długi czas” implementacji testu automatycznego nie musi oznaczać tego samego czasu w różnych organizacjach. Jedni mogą powiedzieć, że taki czas powinien nie przekraczać 4 godzin, inni, że jednego dnia itd. To kwestia bardziej projektowa niż uogólniona definicja trwania implementacji testu. Problem pojawia się wtedy, kiedy testy automatyczne nie nadążają za programowaniem. Konsekwencją tego jest brak wiedzy, czy wersja, która powstała w najnowszej iteracji, nie zawiera błędów regresji. Ten fakt może być spowodowany przez:

a)    framework, który nie umożliwia pisania testów w rozsądnym czasie

b)    przekonanie, że szybciej się nie da, co często jest kłamstwem

c)    pisanie zbyt długich testów, które wykonują wiele kroków, zamiast sprawdzać to, co jest obiektem testu w konkretnej sytuacji

d)    testy stają się coraz trudniejsze w utrzymaniu.

Podpunkt a) oznacza, że framework wymaga zmian. Nie jest to jednak takie proste, ponieważ często nie chcemy wprowadzać zmian, skoro już się przyzwyczailiśmy do takiego sposobu pracy. 

Załóżmy, że testujemy aplikację razem z bazą danych, gdzie dane testowe są importowane do bazy danych za pomocą plików *.csv albo plików *.sql. Takie zarządzanie danymi testowymi jest czasochłonne i uciążliwe. W przypadku zmian w aplikacji takie testy są trudne w utrzymaniu. Warto wobec tego sięgnąć do literatury albo po prostu przeglądnąć Internet i poczytać na temat wzorców programowania, jakie możemy stosować do zarządzania danymi testowymi. Przykładowo, takie wzorce projektowe to: 

  • Obiekt transferu danych (DTO, ang. Data transfer object)
  • Repository, jako warstwa do komunikacji z bazą danych
  • Fluent Interface, zorientowany obiektowo interfejs API, który pozwala nam łączyć wywołania metod w czytelny i intuicyjny sposób
  • The builder design pattern, wzorzec projektowy, który oddziela konstrukcję złożonego obiektu od jego reprezentacji. Klasa Builder implementuje wówczas płynny wzorzec interfejsu i pozwala krok po kroku tworzyć obiekty w postaci listy obiektów tego samego typu
  • Object Mother, zestaw fabryk tworzących obiekty.

Chodzi o to, aby tworzyć dane testowe oraz zarządzać nimi z kodu, a nie importować je z plików. Zapytania do bazy danych oraz buildery obiektów możemy skonstruować tak, aby podawać tylko niezbędne dane dla konkretnej encji (tabeli w bazie), a dla pozostałych danych po prostu ustawiamy wartości domyślne. Wtedy wyraźnie widać co tworzymy i co się zmienia. 

Zastosowanie podejścia, które jest wymienione powyżej, wymaga umiejętności programistycznych. Jako testerzy powinniśmy się zainteresować swoim frameworkiem. To już jest programowanie, a nie pisanie testów. Mamy tutaj kolejny przykład wyjścia ze swojej strefy komfortu. Ciągłe narzekanie na framework nic nie daje. Należy podjąć decyzję o wprowadzeniu zmian i je forsować.

Podpunkt c) może oznaczać, że aplikacja nie jest do końca podatna na testy automatyczne. Taka sytuacja może wystąpić, jeśli aplikacja działa sekwencyjnie, czyli dla testu kroku n musimy wykonać n-1 wcześniejszych kroków. Taki stan rzeczy wymaga zmian w samej aplikacji. Może się to zdarzyć w aplikacjach opartych na bazach danych albo gdy działanie programu jest oparte o długie procedury. Poprawa podatności na testy, czyli umożliwienie uruchamiania części aplikacji, wymaga zaangażowania programistów, a nie tylko osób testujących. Aby dokonać takich zmian, trzeba zacząć pracować inaczej, niż do tej pory, np. pomyśleć o refaktoringu. 

Może się też okazać, że nie potrafimy zlokalizować obiektu testu i wydaje nam się, że powinniśmy testować od początku do końca, bo tak robi klient. Nie jest to prawdą. Zmiany w aplikacji często możemy testować na niższych poziomach, jak już wcześniej pisaliśmy. Jeżeli wymagany jest wyższy poziom testu (z bazą danych), to za pomocą builderów można zamockować wymagane tabelki, uruchomić odpowiedni proces albo funkcję, a następnie sprawdzić rezultat. Nie ma potrzeby testowania całości. Jeśli nie jest to obsłużone we frameworku, to również należy go zmodyfikować i nie musi tego robić programista. Testerzy też powinni znać framework oraz umieć go zmieniać.

Podpunkt b) na ogół wynika z a) oraz c). Jeśli praca jest nieefektywna, to musimy zidentyfikować problemy i je rozwiązać wspólnymi siłami. Nie można zajmować się tylko częścią funkcjonalną produktu. Techniczna praca jest tak samo ważna i powinna iść w parze z rozwojem funkcjonalności. Testerzy automatyzujący nie powinni od niej uciekać.

5.2 Długi czas wykonania testów automatycznych

Czas wykonania testów automatycznych powinien dać w rozsądnym czasie odpowiedź, czy nasza gałąź rozwojowa lub główna gałąź aplikacji nie zawiera błędów regresji. Pojęcie “rozsądny czas” to kwestia ustalenia jego długości w zespole. Można założyć, że ten czas nie powinien przekraczać 30 minut, 1 godziny, lub 1,5 godziny. Natomiast taka sytuacja, w której pół dnia albo całą noc musimy czekać na wyniki testów, nie sprzyja zwinnemu rozwojowi oprogramowania. Istnienie testów automatycznych jako całości ma sens, jeżeli nasz CI w krótkim czasie daje odpowiedź, czy testy przeszły czy nie. Testy komponentowe powinny odpowiedzieć na pytanie czy serwis działa tak, jak powinien oraz zwrócić informacje o pokryciu testami. Od testów integracyjnych czy kontraktowych na ogół będziemy oczekiwać informacji o tym, czy serwisy albo komponenty są kompatybilne między sobą. Od testów e2e będziemy oczekiwali odpowiedzi, czy aplikacja spełnia funkcjonalne wymagania (bardziej skomplikowane w przypadku mikroserwisów).

Firmy zajmujące się wytwarzaniem oprogramowania często mają problem z czasem, który jest potrzebny do uruchomienia wszystkich testów automatycznych. Istnieją jednak pewne zabiegi, które pozwalają poprawić ten czas np.:

a)    uruchamianie testów równolegle

b)    przesunięcie testów na niższy poziom, jeżeli jest to możliwe (shift left)

c)    stawianie środowiska (np. bazy danych) tylko jeden raz, a następnie jego klonowanie dla równoległych przebiegów

d)    pisanie testów akceptacyjnych tak (zwłaszcza do GUI), żeby nie przechodziły one każdego kroku, natomiast testowały tylko to, co jest celem testu (można stosować skróty, jak wstrzykiwanie danych za pomocą API).

Uruchamianie równoległe testów, sposób stawiania środowiska to praca bardziej devopsowa niż testerska. Nie znaczy to jednak, że testerzy nie powinni się w ogóle tym interesować, wręcz przeciwnie. Umiejętność tworzenia, konfigurowania oraz zmiany pipeline’ów powinna być również obiektem zainteresowania pracy testera automatyzującego. 

Devops łączy programowanie i testy. To połączenie jest bardzo ważne zwłaszcza, gdy chcemy wytwarzać oprogramowanie w sposób efektywny. Czas oraz sposób wykonania testów ma ogromne znaczenie w zapewnieniu jakości tworzonego programu, zwłaszcza, gdy automatyzacja jest rozległa – mała ilość lub brak testów manualnych. 

Jeżeli popatrzymy na testy jako całość, to możliwe jest zastosowanie zasady FIRST w kontekście piramidy testów. Ta koncepcja została opisana w artykule pt. “Piramida testów i ciągła integracja”, która znajduje się pod adresem: https://testerzy.pl/baza-wiedzy/artykuly/piramida-testow-i-ciagla-integracja.
Wspomniana wyżej publikacja proponuje kilka rozwiązań, które mogą pomóc w optymalizacji wykonywania czasu testów akceptacyjnych. Możliwe jest również testowanie warstwy GUI jednostkowo, co nie zawsze występuje w organizacjach. Tym rodzajem testów mogą zająć się programiści oraz testerzy, jeśli wcześniej nikt tego nie robił.

6. Podsumowanie

Celem publikacji było uświadomienie czytelnikowi, że będąc testerem automatyzującym, nie możemy się ograniczać tylko do tworzenia skryptów testowych. Automatyzacja testowania to dość szerokie pojęcie i musimy mieć świadomość, że to proces, który nigdy nie jest stały. Zmienia się razem z oprogramowaniem. My, jako testerzy czy programiści, też musimy zmieniać swoje nawyki oraz nie bać się podejmować nowych wyzwań technicznych. 

Recenzenci: Aleksandra Porosińska, Andrzej Kowalski
 

To powinno Cię zainteresować