Bug Test Driven Development

Bug Test Driven Development

To uczucie, gdy kończymy feature, przeklikujemy ostatni raz aplikację, zatrzymujemy się na chwilę, aby nacieszyć się zakończoną pracą, a tu bach! W innej części aplikacji zauważamy bug’a. Co więcej, kojarzymy ten problem, bo już go poprawialiśmy. Pierwsza myśl? Ktoś (bo przecież nie my) znowu to zepsuł!

Nie ma chyba nic bardziej frustrującego niż konieczność wykonania dwa raz tej sam pracy. Jeżeli to dopiero drugi raz to jeszcze nie jest najgorzej. Miałem styczność z projektem, gdzie występował dość złożony kawałek aplikacji. Jedna zmiana w tym obszarze praktycznie zawsze powodowała powrót jednego lub więcej bugów z przeszłości. Frustrująca praca zaczęła się ciągnąć tygodniami.

Im bardziej skomplikowana aplikacja i dłużej rozwijana tym ciężej jest ogarnąć co się dzieje pod spodem. Nie mówiąc już o nowych osobach, które mogą pojawić się w projekcie. Skąd Ci ludzie mają wiedzieć o wszystkich rzeczach, na które wpływa, bądź od których jest zależny dany kawałek kodu? Ok, im kod lepiej utrzymany, mam na myśli jego SOLIDność, tym łatwiej jest się w nim odnaleźć. Jednak nie zmienia to faktu, że coś możemy przeoczyć. W językach statycznie typowanych jest ciężej, jednak wkraczając w świat JS’a, szanse na przeoczenie czegoś rosną wykładniczo.

Jak upewnić się, że pokonaliśmy buga raz na zawsze?

Nikt z nas nie chce przecież w kółko wykonywać tej samej pracy. Nie na tym polega rozwój software’u. Kluczem tutaj są zautomatyzowane testy. W rzeczywistości bywa z nimi różnie. Idealnie byłoby mieć pokryty kod w 90% lub więcej, zazwyczaj niestety nie wygląda to tak kolorowo. Albo testów nie ma wcale albo są takie testujące tylko jeden scenariusz albo ktoś kiedyś zaczął je pisać, ale potem terminy zaczęły gonić i testy poszły w zapomnienie.

Bug Test Driven Development*

Tak, czy inaczej warto trzymać się jeden zasady – pojawił się bug? To piszemy na niego test. Pokrywając problem testem mamy pewność, że ktoś, zapewne nieumyślnie, znowu go nie spowoduje. Żeby być bardziej precyzyjnym – błąd spowodować może, ale zapali mu się czerwona lampka i będzie wiedział, że coś zepsuł przy okazji swojej pracy.

Test stanowi również pewien rodzaj dokumentacji problemu. Najpierw udowadnia, że bug faktycznie występuje w kodzie. Następnie przedstawia scenariusz w jakim problem miał miejsce. Ostatecznie, gdy dokonamy poprawek w kodzie, daje nam pewność, że problem został wyeliminowany raz na zawsze.

Jeżeli już poświęcimy czas na żmudne debugowanie i naprawienie problemu warto zostawić po sobie taki ślad. Całkiem możliwe, że w przyszłości ktoś nie będzie musiał drugi raz wykonywać całej tej pracy ponownie.

Sposób na wprowadzenie testów do projektu

Jeżeli ktoś jest w sytuacji, gdzie nie ma prowadzonych żadnych testów i/lub brak w zespole umiejętności, czy kultury ich pisania, pokrywanie występujących błędów testami jest zawsze jakimś sposobem na wprowadzenie testów do projektu. Nie jest to idealne podejście, ale lepsze takie niż żadne.

Często również w prostych aplikacjach CRUD, gdzie mamy prostą architekturę typu: baza danych -> repozytorium -> serwis -> controller, a większość logiki biznesowej to przepisywanie modelu na data contract i na odwrót, możemy się zniechęcić do pisania testów od podstaw (mam na myśli testy do napisanego już kodu, nie podejście TDD, które odwraca całkowicie koncepcje tworzenia aplikacji).

Dlaczego może tak się zdarzyć?

Może dość kontrowersyjne stwierdzenie, ale można odnieść wrażenie na pierwszy rzut okna, że testy do takiego kodu za wiele nie wnoszą. Jest to w pewnym stopniu uzasadnione, bo prawdziwa wartość tych testów może ukazać się dopiero po czasie. Podczas dalszego rozwoju aplikacji, czy wdrażaniu nowego programisty. Możemy w takiej sytuacji spróbować zacząć wprowadzać testy do projektu pokrywając nimi występujące bugi.

Dlaczego łatwiej będzie zacząć pisać testy pokrywające bugi?

Między innymi dlatego, że w przypadku poprawiania bug’a od razu widzimy wartość tego testu. Dodając półżartem – może te same błędy pojawiające się kolejny raz bolą bardziej niż nowe?:) A tak bardziej na poważnie – lepiej zacząć późno niż wcale i traktować to jako drogę do wejścia w nawyk pisania testów.

Na zakończenie

Przytoczone tutaj zalety pokrywania błędów testami tyczą się oczywiście ogólnie testów, nie tylko tych pisanych na potrzeby konkretnego bug’a. Czemu więc zdarza się pisać aplikacje niepokryte testami? Pierwsze dwie rzeczy jakie przychodzą mi do głowy to lenistwo i brak świadomości jakie tak naprawdę pociąga to za sobą (początkowo ukryte) koszty. Może pokrywanie chociaż błędów testami to jakiś sposób na szybsze przekonanie się o prawdziwej ich wartości?

*Nazwa oczywiście wymyślona na poczekaniu, nie znalazłem żadnego terminu wśród tych najpopularniejszych typu TDD, który opisywałby to podejście. Najbliżej pasujący (chodź nie do końca), istniejący termin to testy regresyjne.

  • Kiedyś miałem bardzo sceptyczne podejście do testów, do czasu gdy udało mi się znaleźć wiele bug-ów, na które bym po prostu nie wpadł. Jeżeli na danym projekcie jest możliwość pisania testów, to polecam w 100%.

    • Przemek Smyrdek

      To sceptyczne podejście o którym piszesz może polegać na braku zrozumienia istoty testowania – kiedy ktoś przedstawia nam testy jako najprzyjemniejszą część pracy programisty (wut), albo testy w podejściu fanatycznym, to jedyne czego można oczekiwać to zniechęcenie słuchacza danym tematem. Do tego dochodzi kultura pracy – jeśli ktoś rozumie, że testowanie to integralna część pracy programisty to w trakcie np. estymowania danego zadania nie trzeba opowiadać głupot w stylu “X, a z testami 2X” bo w taki sposób utrwalamy w sobie jedynie negatywne skojarzenia z tym tematem, zamiast skupiać się na tym co testy nam dają.