Każdy software wymaga testowania, jednak proces ten może przyjmować bardzo różne formy. Niektóre sposoby testowania są gorsze, a inne lepsze. Najgorszym możliwym sposobem jest pozostawienie wszystkich testów użytkownikom aplikacji zgodnie z mottem „dopóki nie słyszymy krzyków znaczy, że wszystko działa jak należy”. Jednak w rzeczywistości taki stan rzeczy występuje bardzo rzadko – każdy programista, który pisze jakiś kod mimochodem sam go – przynajmniej częściowo – testuje. Żeby napisać program trzeba go po drodze odpalać* – co jest przecież pewną formą sprawdzenia czy kod działa.
*Niech pierwszy rzuci kamieniem ten, kto nigdy nie opublikował żadnej drobnej zmiany bez odpalenia jej na lokalnej maszynie.
Na początku były testy manualne
Załóżmy, że pracujemy w szanującej się firmie programistycznej, która zatrudnia chociaż kilku testerów manualnych. W takiej sytuacji możemy wynieść nasze procedury testowania oprogramowania na kolejny poziom – możemy zacząć robić testy manualne. W przypadku testów manualnych rola testera jest nieodzowna – stare porzekadło mówi, że programista nie powinien sam testować swojego kodu. Za każdym razem, gdy tak się dzieje – dziwnym trafem – przetestowane są tylko happy-path’y i nic więcej. Natomiast tester, taki z prawdziwego zdarzenia, złośliwy, dociekliwy osobnik – zrobi co tylko w jego mocy, aby jakieś bugi programiście znaleźć. Dla programisty jest to doświadczenie niezwykle przykre – sam dobrze o tym wiem będąc programistą. Nic tak nie boli jak naprawianie bug’a wytkniętego nam przez buńczucznego testera. Jednak dla firmy i dla użytkowników jest to niezwykle pożyteczne.
Procedura wytwarzania oprogramowania z udziałem testów manualnych jest dziecinnie prosta:
1. Programista pisze kod nowej funkcji (feature)
2. Tester sprawdza nowy ficzer, skrupulatnie zgłaszając wszystkie bugi i niedociągnięcia
3. Programista poprawia kod
4. Tester potwierdza, że poprzednie bugi zostały naprawione, ale za to pojawiły się nowe
5. Programista poprawia nowe bugi
6. (wróć do punktu 4.)
7. Wszystkie bugi są poprawione, programista i tester są szczęśliwi, że razem zbudowali nowy ficzer. Tester daje ‘approve’ i całość może zostać zdeployowana (opublikowana) na produkcję
No czyż nie jest to proste? Ilość cykli „programowanie -> testy -> nowe bug’i” jest zależna od doświadczenia zespołu i zazwyczaj wyrażona liczbą o skończonej wartości.
Dlaczego testy manualne są niewystarczające
Problem przy testach manualnych pojawia się w momencie, gdy ktoś w zespole wpada na pomysł zrobienia regresji. Regresja są to testy, które mają potwierdzić, że wszystkie wybudowane do tej pory ficzery dalej działają poprawnie. Jak każdy zapewne może sobie wyobrazić, liczba funkcji w danym oprogramowaniu jest wprost proporcjonalna do ilości czasu jaki ten soft jest już wytwarzany. A proszę pamiętać, że przyszło nam żyć w czasach, w których wytwarzanie softu nie kończy się nigdy – zjawisko to nazywa się „agile” – ale o tym, kiedy indziej. Software w firmie, w której obecnie pracuję wytwarzany jest od 25 lat. Kolega z pracy, który pracuje tam od początku, napisał swoją pierwszą firmową linijkę kodu, gdy miałem 6 lat. Wyobraźcie sobie teraz, że musicie nagle odpowiedzieć na z pozoru łatwe pytanie: „czy system działa?”. Tego niestety nie wie praktycznie nikt, bo nikt już nie pamięta jakie w ogóle są wszystkie funkcje tego systemu – tak wiele różnego kodu powstało przez te 25 lat.
Każdy tester manualny musi przygotować się na życie w pewnej niepewności, niedookreśloności. Nigdy nie będziecie do końca znać odpowiedzi na pytanie „czy system działa”. Być może jakiś jego fragment, w jakimś stopniu działa. Najgorsze w tym wszystkim jest to, że to wy będziecie osobami, które muszą dać zielone światło: „tak! Możecie wrzucać na produkcję!”.
Co gorsza, z każdą kolejną linijką kodu, która napiszą programiści wy macie kolejną linijkę kodu, o której musicie pamiętać podczas testów. Trochę jak w filmie Gattaca (1997) o dwóch braciach. Antonie - zmodyfikowanym genetycznie, pozbawionym wszelkich wad twardzielu (programista) i drugim, Jeromie, zwykłym chłopku roztropku, który marzył o tym, żeby polecieć w kosmos (tester). Od maleńkości grali ze sobą w grę „kto pierwszy spęka” wypływając w otwarte morze wpław. Jakże trafnie słowa Jeroma oddają sytuację testerów manualnych:
It was the last time we swam together.
Out into the open sea, like always,
knowing each stroke towards the horizon
was one we had to make back to the
shore. Like always, the unspoken contest.
Jednak tym razem rzeczywistość jest łaskawsza od filmowej fikcji, ponieważ testerzy w przeciwieństwie do Jeroma mają coś, co wyrównuje ich straceńczą walkę z programistami – testy automatyczne.
Czym są testy automatyczne
Wyobraźcie sobie testera na sterydach. Nie musi spać, nie musi jeść, znajdzie buga w parę minut i nie pobiera pensji. Umysł testerski zaklęty w maszynie. Testy automatyczne to nic innego jak mini-programiki, które replikują manualne czynności dokonywane przez testera w poszukiwaniu błędów. Taki test automatyczny potrafi odtwarzać pewne scenariusze i stwierdzić czy „zadziałało” czy „nie zadziałało”. Weźmy ekran logowania – test automatyczny włączy przeglądarkę, kliknie w pole „email”, wpisze tam jakiś email, kliknie w pole hasło, wpisze hasło, a na końcu nadusi „zaloguj”. Wszystko tak szybko, że ciężko za tym nadążyć obserwując okno przeglądarki. Na końcu sprawdzi, czy przeniosło go w dobre miejsce i zakończy działanie. Takich testów można mieć dziesiątki a nawet setki – każdy sprawdzający jakąś inną drobną (bądź dużą) funkcję systemu.
Optymalne podejście do pisania testów automatycznych powinno wyglądać tak, że za każdym razem, gdy w systemie pojawia się nowa funkcja, dopisujemy kolejny ich zestaw do naszej biblioteki testów. Dzięki temu, wraz z upływem czasu mamy ich coraz więcej, a sam proces developmentu i testów oprogramowania zaczyna wyglądać naprawdę przyjemnie. Programista wypuszcza nowy fragment systemu, a my odpalamy nasze testy i widzimy, że tu, tu i tu testy przestały przechodzić. Z taką informacją wracamy do programisty, a on wprowadza konieczne poprawki i tak dalej. W międzyczasie oczywiście dopisujemy nowe testy.
Posiadanie bogatej biblioteki testów ma wiele zalet, ale największą z nich jest poczucie bezpieczeństwa, które daje zespołowi. Możemy zacząć wypuszczać nowe rzeczy na produkcję naprawdę szybko – bez większych obaw, że odkryjemy jakieś przeoczone bugi dopiero gdy usłyszymy krzyk naszych użytkowników. A bugi potrafią pojawiać się w naprawdę nieoczywistych i dość odległych miejscach od fragmentu, w którym były wprowadzane zmiany w kodzie. Zwłaszcza w wiekowych systemach, o których nikt do końca nie wie, jak działają.
Wady testów automatycznych
Niestety, testy automatyczne mają też pewne wady, o których musimy pamiętać. Po pierwsze – to nie do końca jest tak, że raz napisane będą nam służyć w nieskończoność. Trzeba je utrzymywać. Wyobraźmy sobie, że mieliśmy 10 testów sprawdzających logowanie do systemu, a zespół postanowił przerobić trochę sposób działania tej formatki. BUM! Nagle 10 naszych testów jest czerwonych (nie przechodzą). Okazało się, że programista zmienił id pola login i testy nie umieją sobie poradzić z wpisaniem danych do logowania – trzeba do nich wrócić i je poprawić. Od teraz do każdej zmiany, którą planujemy musimy doliczyć pewien czas na poprawienie i dostosowanie istniejących testów automatycznych. Dla fanatyków excela i managerów z bożej łaski może być to trudne do przełknięcia – ale prawda jest taka, że ten czas i tak byśmy poświęcili w ten lub inny sposób na testowanie nowych zmian (albo na gaszenie pożarów na produkcji, jeżeli nie testujemy, bo ‘nie mamy na to czasu’).
Drugą, dość oczywistą wadą testów automatycznych jest to, że trzeba umieć je napisać. A niestety nie jest to taka prosta sprawa. Moim zdaniem pisanie dobrych testów automatycznych to jedno z trudniejszych programistycznych wyzwań. A złych testów nie potrzebujemy. Wiecie co jest gorszego od braku testów? Testy, które kłamią i dają nam fałszywe poczucie bezpieczeństwa. Albo testy, które są tak wrażliwe na zmiany, że co chwila nie przechodzą. Gorzej – testy które nie przechodzą w losowy sposób, albo ich działanie bądź niedziałanie ma jakieś zależności czasowe. Pamiętam przypadek, że niektóre nasze testy nie działały przed 6.00 rano – jak później odkryliśmy wynikało to z pewnych różnic w strefach czasowych. Podsumowując: testy, którym nie możemy ufać są tylko kulą u nogi zespołu. Dostrzegam tu także pewną ironię losu - żeby móc skutecznie ‘walczyć’ z programistami musicie w zasadzie stać się jednymi z nich.
Podsumowanie
Wracając do filmu ‘Gattaca’ – Jerome poświęcił bardzo wiele i narzucił sobie olbrzymią dyscyplinę, żeby móc konkurować z genetycznie ulepszonym bratem Antonem i spełnić swoje marzenie o podróży w kosmos. Uważam, że z pisaniem testów automatycznych sytuacja jest podobna – nie jest to zadanie proste i wymaga sporej dyscypliny. Jednak efekty są warte tej ceny. Sam jestem wielkim fanek wszelkiego rodzaju testów automatycznych. Testów jednostkowych, testów integracyjnych, oraz testów end-to-end (e2e), o których dzisiaj pisałem bez podawania ich fachowej nazwy. Zakończenie sceny, której fragment przytoczyłem wcześniej, jest znamienne. Ten jeden raz to Anton spękał i niemalże utonął – gdyby nie pomoc brata, który wyciągnął go ostatkiem sił z morza i uratował mu tyłek. Dokładnie tak samo jest w IT – nie raz, nie dwa to właśnie czujne oko testera i starannie napisany przez niego test uratowały mnie prze kompletnym zrujnowaniem produkcji.
Comentários