Najlepsze praktyki dotyczące strategii testowania dla zespołu Scrumowego

Photo of author

By maciekx

Scrum bez planu testowania to nic innego jak projekt Proof of Concept (POC) na dopalaczach.

Współcześnie wiele udanych przedsięwzięć rozpoczyna się od fazy weryfikacji koncepcji (POC). Jest to relatywnie niewielka ocena pomysłu, podczas której wybrana technologia i zakres funkcji są poddawane wstępnej analizie. Sprawdza się potencjalny wpływ na odbiorców biznesowych, a następnie, jeśli idea okaże się obiecująca, dedykowany zespół projektowy przystępuje do zaprojektowania i dostarczenia w pełni funkcjonalnego produktu, gotowego do wdrożenia produkcyjnego.

W takim scenariuszu doskonale sprawdza się metodologia Scrum. Zespół może błyskawicznie opracować prototyp, wzbogacając go o kluczowe nowości w każdym sprincie. Odbiorcy biznesowi mają możliwość obserwowania w czasie rzeczywistym, jak koncepcja nabiera kształtu, w zaledwie kilka sprintów. W rezultacie powstaje mocne wrażenie (co samo w sobie jest celem POC), lecz brakuje jednego, kluczowego elementu – testowania. Myślenie o procesie testowym w takiej sytuacji można uznać za swoisty żart.

W zespołach Scrumowych temat testowania nie jest traktowany priorytetowo. Członkowie zespołu preferują ideę rozwoju bez procesów, które mogą ich spowalniać, a testowanie jest często postrzegane właśnie w ten sposób. Nikt nie chce niepotrzebnych opóźnień, zwłaszcza gdy zależy nam na jak najszybszym wywarciu wrażenia na odbiorcy końcowym.

Sytuację, w której zespół projektowy kontynuuje pracę po zakończeniu fazy POC w podobny sposób, nazywam „POC na sterydach”. System produkcyjny rozrasta się pod względem rozmiaru i funkcjonalności, lecz wciąż przypomina POC – niekompletny produkt z ukrytymi wadami i niezbadanymi przypadkami brzegowymi, które tylko czekają na poważną awarię.

Z każdym kolejnym sprintem zespołowi coraz trudniej jest utrzymać stabilność wydań, ponieważ naprawianie problemów staje się coraz bardziej skomplikowane.

Poniżej przedstawiam kilka technik, które sprawdziły się w podobnych sytuacjach i które uważam za najlepsze praktyki we wdrażaniu solidnych procesów testowania. Jednocześnie nie blokują one nadmiernie postępu w programowaniu, ponieważ wszyscy chcemy, aby nasz zespół działał w duchu Scrum.

Podzielmy się „bólem”

Jak zacząć, gdy musimy uporać się z problemem, który wydaje się trudny do obejścia? Najlepiej zacząć od podzielenia się tym „bólem” :).

Mam na myśli stworzenie planu, który wymaga od programistów niewielkiej dodatkowej aktywności tu i ówdzie, ale który z czasem znacząco przyczyni się do osiągnięcia wspólnego celu, stopniowo, lecz konsekwentnie.

#1. Twórz testy jednostkowe dla każdej nowej części kodu

Jeżeli uda się przekonać zespoły Scrumowe do dodania do definicji „ukończoności” (Definition of Done) wymogu tworzenia testów jednostkowych dla każdego nowego kodu powstającego w sprincie, zyskamy ogromne korzyści w dłuższej perspektywie.

Powody są oczywiste:

Zmusimy programistów do analizy różnych, nietypowych ścieżek w kodzie.

  • Testy jednostkowe można włączyć do zautomatyzowanych potoków DevOps, uruchamianych przy każdym wdrożeniu do środowiska deweloperskiego lub testowego. Ponadto metryki z potoku można łatwo wyeksportować i zaprezentować użytkownikom biznesowym w formie procentowego pokrycia testami przypadków bezpośrednio związanych z kodem źródłowym.

Kluczowe jest, aby zacząć jak najszybciej. Im później rozpocznie się praca nad testami jednostkowymi, tym bardziej rozpraszające będzie dodawanie ich w trakcie sprintu.

  • Opracowanie testów jednostkowych dla istniejącego kodu wymaga znacznego wysiłku. Niektóre części kodu mogą być tworzone przez innych programistów, a dokładna wiedza o działaniu poszczególnych fragmentów nie jest jasna dla obecnego dewelopera. W niektórych przypadkach dodanie testu jednostkowego do zmodyfikowanego kodu może zająć więcej czasu niż opracowanie zmiany funkcjonalności w sprincie. Takiego scenariusza na pewno nikt nie planował w trakcie sprintu.

#2. Uczyń rutyną uruchamianie wszystkich testów jednostkowych w środowisku developerskim

Jeszcze przed utworzeniem żądania scalenia nowego kodu z gałęzią główną, standardem powinno być przetestowanie zarówno kodu funkcji, jak i testów jednostkowych w środowisku developerskim. Zapewni to, że:

  • Testy jednostkowe faktycznie działają dla każdej części (w końcu to też kod, który należy sprawdzić). Ten krok jest często pomijany. Zakłada się, że skoro test jednostkowy został włączony do potoku DevOps, to w końcu zostanie wykonany i przetestowany. W rzeczywistości przenosimy problem na wyższe poziomy działania sprintu, gdzie zespół ma zwykle mniej czasu i więcej stresu związanego z realizacją każdej historii.
  • Nowy kod funkcji jest testowany przez programistę pod kątem podstawowej funkcjonalności. Oczywiście nie oczekujemy w tym miejscu pełnej weryfikacji biznesowej, lecz upewniamy się, że kod zachowuje się zgodnie z intencjami dewelopera (pomijając na razie fakt, że logika biznesowa mogła zostać źle zrozumiana w trakcie implementacji).

#3. Oczekuj wykonania testów jednostkowych po scaleniu kodu z gałęzią główną

Jedną rzeczą jest posiadanie działającego kodu w lokalnej gałęzi (gdzie nikt, oprócz jej autora, nie rozwija nowej funkcji), a zupełnie inną, gdy ten sam kod działa po przesłaniu żądania scalenia z gałęzią główną.

Gałąź główna zawiera zmiany od innych członków zespołu Scrum. Nawet jeśli konflikty zostaną rozwiązane i wszystko wydaje się w porządku, kod po scaleniu i rozwiązaniu konfliktów nadal jest nieprzetestowany. Wysłanie go dalej bez wcześniejszego sprawdzenia, czy działa poprawnie, jest ryzykowne.

Skuteczne okazało się ponowne uruchomienie tych samych testów jednostkowych, które wcześniej były wykonywane w środowisku developerskim, na środowisku zbudowanym z wersji kodu gałęzi głównej.

Dla programistów może to być dodatkowy krok, ale nie wymaga on dużego nakładu pracy. Nie trzeba wymyślać niczego nowego – wystarczy ponownie wykonać to, co już raz zostało zrobione.

Krok ten można pominąć w jednym konkretnym przypadku:

  • Zautomatyzowane testy jednostkowe w potokach DevOps są tak wszechstronne, że obejmują wszystkie testy ręczne wykonywane na ich podstawie.

Osiągnięcie takiego stanu jest możliwe, lecz rzadko spotykane. Tworzenie tak szczegółowych automatycznych testów jednostkowych zajęłoby zbyt wiele czasu programistów. Właściciel produktu prawdopodobnie nie zgodziłby się na poświęcenie tyle czasu na testowanie, ponieważ miałoby to negatywny wpływ na liczbę dostarczonych historii w sprincie.

Jednak preferowanie ilości nad jakością nie powinno być wymówką do unikania testów jednostkowych, nawet w minimalnym zakresie. Zespół ponownie znajdzie się w sytuacji, gdy kod będzie miał zbyt małe pokrycie testami. Nadrobienie zaległości może okazać się bardziej kosztowne niż sama zmiana kodu w sprincie.

Z tych powodów gorąco polecam ponowne uruchomienie testów jednostkowych na wersji kodu z gałęzi głównej. To niewielki wysiłek, a korzyści są ogromne.

Pozwoli to na ostateczne sprawdzenie, czy gałąź główna jest gotowa do fazy testowania wersji. Pomaga również w znalezieniu większości błędów technicznych, dzięki czemu w kolejnej fazie można skupić się wyłącznie na weryfikacji funkcjonalności biznesowej.

Przygotuj się do testów funkcjonalnych

Dotychczasowe działania związane z testowaniem mają jeden cel – upewnienie się, że kod w gałęzi głównej jest wolny od błędów technicznych i może być uruchamiany bez problemów w pełnych przepływach funkcjonalnych.

Tą fazę może z łatwością przeprowadzić jedna osoba, która nie musi mieć nawet technicznego zaplecza.

W rzeczywistości lepiej, gdy jest to osoba niezwiązana z programistami, ale posiadająca wiedzę na temat oczekiwań użytkowników biznesowych wobec kodu. Należy wykonać dwie główne czynności:

#1. Ukierunkowane testy funkcjonalne nowych historii sprintu

Idealnie każdy sprint powinien dostarczać nową funkcjonalność (przyrost celu sprintu), która wcześniej nie istniała i która powinna być zweryfikowana. Ważne jest, aby nowe oprogramowanie działało w taki sposób, by zadowolić odbiorców biznesowych, którzy czekali na nie co najmniej przez ostatni sprint.

Smutne jest doświadczenie, gdy (długo) obiecywana funkcja zostaje wreszcie udostępniona, aby dowiedzieć się wkrótce, że nie działa ona poprawnie.

Dlatego tak ważne jest odpowiednie przetestowanie nowej funkcjonalności sprintu. Dobrą praktyką jest zebranie z wyprzedzeniem ważnych przypadków testowych od odpowiednich interesariuszy (właściciela produktu lub nawet użytkowników końcowych) i stworzenie listy potrzebnych testów dla zawartości sprintu.

Na pierwszy rzut oka może to wydawać się przytłaczające, lecz z doświadczenia wiem, że jest to możliwe do zrealizowania przez jedną osobę. Sprinty są zazwyczaj dość krótkie (np. dwutygodniowe), a nowych treści nie jest zbyt wiele, gdyż sprint zawiera również inne działania, takie jak historie długu technicznego, dokumentacja, projekty, itp.

Następnie wykonujemy testy przypadków, których celem jest weryfikacja pożądanej funkcjonalności. W przypadku problemów zwracamy się do odpowiedniego programisty (odpowiedzialnego za daną zmianę) z prośbą o szybkie rozwiązanie problemu.

W ten sposób programiści minimalizują czas spędzony na testowaniu funkcjonalnym i mogą skupić się na tym, co lubią najbardziej.

#2. Wykonywanie testów regresji

Kolejnym aspektem testów funkcjonalnych jest upewnienie się, że to, co działało do tej pory, będzie działać także w nowej wersji. Do tego służą testy regresji.

Przypadki testowe regresji powinny być regularnie aktualizowane i przeglądane przed każdym wydaniem. W zależności od specyfiki projektu, najlepiej, aby były one proste, ale obejmowały najbardziej podstawowe funkcje i ważne przepływy „end-to-end” w całym systemie.

Zwykle każdy system ma procesy, które obejmują wiele różnych obszarów. Są to idealni kandydaci do przypadków testowych regresji.

W niektórych przypadkach testy regresji mogą domyślnie obejmować również nowe funkcje sprintu, np. gdy nowa historia zmienia część istniejącego przepływu.

Dlatego najczęściej nie jest trudno przeprowadzić testy regresji razem z testami funkcjonalności nowych historii sprintu, zwłaszcza, gdy robi się to regularnie przed każdym wydaniem produkcyjnym i gdy cykl wydawania nie jest bardzo krótki.

Nalegaj na testy kontroli jakości (QA) przed każdym wydaniem produkcyjnym

Test QA jest ostatnim krokiem przed wdrożeniem i często jest pomijany jako nieważny, zwłaszcza gdy zespół Scrum jest pod presją dostarczania nowych treści.

Odbiorcy biznesowi często mówią, że interesują ich nowe funkcje, a nie zachowanie funkcjonalności lub niska liczba błędów. Z czasem prowadzi to do spowolnienia pracy zespołu. Odbiorcy biznesowi i tak nie dostaną tego, o co proszą.

Test QA powinien być wykonywany przez osoby spoza zespołu Scrum, najlepiej przez samych odbiorców biznesowych, w środowisku jak najbardziej zbliżonym do produkcyjnego. Alternatywnie właściciel produktu może zastąpić użytkowników końcowych.

Powinien to być test z perspektywy użytkownika końcowego, bez powiązania z zespołem Scrum. Przedstawia on ostateczny widok produktu i jest testowany w sposób, którego prawdopodobnie nikt z zespołu Scrum nie przewidział. Jest to ostatnia szansa na wprowadzenie poprawek.

Może się okazać, że oczekiwania nie zostały dobrze zrozumiane przez zespół Scrum. W takim przypadku przynajmniej można ustalić plan działania naprawczego, zanim jeszcze wersja trafi na produkcję. Jest to lepsze rozwiązanie niż przyznanie się do porażki po wdrożeniu.

Co dalej?

Stosowanie tych praktyk w codziennej pracy ze Scrumem może przyczynić się do bardziej stabilnych i przewidywalnych wydań, bez opóźniania ich, bez spędzania całego sprintu na przygotowywaniu wydania i bez zmuszania wszystkich członków zespołu do robienia tego, co nie do końca lubią.

Oczywiście na tym nie można poprzestać.

Warto wspomnieć o włączeniu testów wydajnościowych. Często są ignorowane lub uważane za niepotrzebne. Zawsze miałem wątpliwości, jak można oczekiwać, że system będzie się rozwijał, jeśli nie będzie regularnie testowany pod względem wydajności.

Krok dalej to wprowadzenie testów automatycznych.

Omówiłem już automatyzację testów jednostkowych. Można jednak opracować pełne testy regresji „end-to-end”, które będą całkowicie zautomatyzowane i uruchamiane po każdym wdrożeniu w środowisku testowym. Odciąży to zespół deweloperski, ale wymaga dedykowanego zespołu do opracowania i utrzymania testów automatycznych. Będzie to ciągła praca, ponieważ zmiany w kodzie produkcyjnym mogą unieważnić niektóre testy automatyczne i będą wymagały aktualizacji. Jest to wydatek, na który tylko nieliczni są gotowi, lecz korzyści dla zespołu programistycznego są ogromne.

To już wykracza poza zakres tego artykułu. Trzeba ustalić harmonogram dla każdego rodzaju testów, tak aby zmieścić się w oknie sprintu. Chętnie zagłębię się w ten temat następnym razem!


newsblog.pl