TDD - okładka

O Test Driven Development

W jednym z ostatnich wpisów na blogu poruszałem zagadnienia związane z testowaniem oprogramowania. Jednym z zagadnień, które pominąłem w tamtym artykule było podejście Test Driven Development, w skrócie TDD. W tym poście powiem nieco więcej o tym podejściu. Jednakże do pełnego zrozumienia wpisu warto wcześniej zapoznać się z moim poprzednim artykułem dotyczącym testowania oprogramowania – Podstawy testów automatycznych oprogramowania.

Geneza Test Driven Development

Myślę, że dobrym punktem startu będzie nakreślenie rysu historycznego. Wbrew pozorom sama idea TDD jest starsza niż mogłoby się początkowo wydawać. Pierwsze wzmianki o technice noszącej znamiona TDD możemy znaleźć w bardzo starych książkach traktujący o programowaniu tj. “Digital Computer Programming”1957 roku. Wzmiankę o czymś na wzór TDD możemy także znaleźć w raporcie z konferencji sponsorowanej przez Komitet Naukowy NATO1968 roku:

A software system can best be designed if the testing is interlaced with the designing instead of being used after the design.

Niemniej jednak wtedy nie nosiło to miana Test Driven Development. Termin TDD został spopularyzowany przez 2 książki autorstwa Kenta Becka:

Obecnie termin ten jest tak popularny, że natknąłem się w sieci nawet na kursy, warsztaty i szkolenia skupiające się na temacie TDD. Co ciekawe, niektóre z nich potrafią kosztować naprawdę spore pieniądze!

Czym jest TDD?

Opowiadanie o historii TDD bez wytłumaczenia na czym ono polega byłoby faux pas z mojej strony, także szybko naprawię ten błąd.

W skrócie – Test Driven Development jest to technika wytwarzania oprogramowania, w której najpierw tworzymy test. Dopiero potem tworzy się kod sprawiający, że test przejdzie pomyślnie.

Procedura wytwarzania kodu metodą TDD składa się z trzech etapów: red, green oraz refactor:

Test Driven Development -Red green refactor diagram

 

Red – na samym początku tworzony jest test. Na przykład dla kalkulatora mógłby to być test dodawania. Rzeczą oczywistą jest, że uruchomienie takiego testu da wynik negatywny. W końcu test sprawdza niestniejący kod. Dla wspomnianego kalkulatora test napisany w TypeScript mógłby wyglądać następująco:


import { expect } from 'chai';

import SimpleCalculator from '../src/SimpleCalculator';

describe( 'SimpleCalculator - Unit tests' () => {
  let calculator: SimpleCalculator;

  before( () => {
    calculator = new SimpleCalculator();
  } );

  describe( 'add()', () => {
    it( 'should add two numbers and return a correct result', () => {
      expect( calculator.add( 2, 2 ) ).to.eql( 4 );
    } );
  } );
} );

Green – w tej fazie do istniejącego testu wytwarzany kod, który sprawi, że test przejdzie pomyślnie. Poniżej przedstawiam przykładowy kod:


export default class SimpleCalculator {
  public add( a: number, b: number ): number {
    return a + b;
  }
}

Jak możesz zauważyć brakuje tutaj innych metod, których można by się spodziewać w kalkulatorze. Jest to zabieg celowy. Istotne jest to, aby NIE pisać kodu, którego użyteczność wykracza poza wcześniej napisany test.

W tej fazie nie jest istotna wydajność ani elegancja kodu. Jedynym jego zadaniem jest sprawienie, aby test przeszedł pomyślnie.

Refactor – faza refaktoryzacji. W tej fazie można zoptymalizować i usprawnić wcześniej wspomniany kod. Wciąż nie należy zapominać o wcześniej wspomnianej zasadzie – nie wolno wykraczać poza to co obejmuje test. Kod z przykładu jest na tyle prosty, że fazę refactor w tym przypadku mogę pominąć.

Po ukończeniu cyklu możemy rozpocząć kolejną iterację poprzez napisanie testu na przykład dla odejmowania. Cykl ten powinien być powtarzany tak długo aż uzyskamy finalną aplikację.

Zalety i wady TDD

Niewątpliwą zaletą TDD jest możliwość wczesnej detekcji i eliminacji błędów. Oprócz tego mając już zestaw testów, która przeszła przez 3 fazy zwiększa się odporność systemu na regresję. Jeżeli w fazie green uruchomiony kod spowoduje porażkę innego testu, to jest to oczywisty dowód na powstanie regresji w kodzie.

Kolejnym benefitem jest to, że zgodnie z TDD każda linijka kodu który stworzyliśmy jest przetestowana, co daje pokrycie kodu na poziomie 100%. Oczywiście do sprawdzenia pokrycia kodu możemy użyć narzędzi tj. Istanbul ale TDD również nam to zapewnia. Warto pamiętać, że dla biznesu 100% code coverage może być ważnym czynnikiem a niekiedy kartą przetargową przy pozyskiwaniu klientów.

Korzystanie z TDD zwiększa też świadomość programisty nad tym co tworzy oraz jak działa projektowany system. Zaczynanie programowania od testów daje szerszą perspektywę nie tylko na wartość biznesową którą ma dawać tworzony kod, ale też na architekturę projektowanego systemu.

Jeśli zaś chodzi o wady TDD to dla początkujących pisanie testu do nieistniejącego kodu może stanowić wyzwanie. Niemniej jednak po kilku napisanych testach w podejściu TDD nie powinno to stanowić większego problemu.

Trudne może okazać się ciągłe pamiętanie, żeby nie dopisywać więcej kodu niż jest to potrzebne. Pracując w TDD sam bardzo często się na tym łapałem.

Nie widzę też zastosowania dla TDD w przypadku testów sprawdzających trywialny kod tj. kod z przykładów z tego wpisu. Oczywiście nie twierdzę, że nie można tu użyć podejścia TDD. Twierdzę jednak, że wartość dodana płynąca z użycia TDD w porównianiu z tradycyjnym podejściem w tym przypadku będzie znikoma.

Warto też pamiętać, że TDD to nie jest jedyna słuszna droga, tylko narzędzie mające pomóc programiście. Jeżeli czujesz że TDD nie jest dla Ciebie to nie jest to absolutnie nic złego.

Podsumowanie

Bardzo jestem ciekaw czy miałeś/aś już styczność z TDD, a jeśli tak to co o nim sądzisz. Zachęcam do pozostawienia komentarza. Sam osobiście nie programuję zbyt często w oparciu o TDD, jednak podczas pisania tego artykułu uznałem, że być może warto to zmienić! Jak zwykle zachęcam też do zapoznania się z materiałami dodatkowymi i źródłami.

Źródła i materiały dodatkowe: