Poniższy artykuł wprowadza w tematykę rozwoju oprogramowania zgodnie z podejściem Test-Driven Development. TDD zostanie przedstawione w teorii, a także na przykładzie, co pozwoli na szybsze zrozumienie zagadnienia.
Czym jest TDD?
TDD jest podejściem do rozwoju oprogramowania, w którym programista rozpoczyna pracę od pisania testu. Kolejnym krokiem jest stworzenie kodu produkcyjnego przechodzącego dany test. Na końcu pojawia się faza refaktoryzacji, w której porządkuje się kod logiki biznesowej i testu. Cały cykl zwie się interacją.
Testowanie oprogramowania udowadnia istnienie błędów, a nie ich brak.
– Autor nieznany
TDD w praktyce
Załóżmy, że tworzymy sklep internetowy. Użytkownik, który chciałby z niego korzystać musiałby się uprzednio zarejestrować, a także aktywować konto potwierdzając adres e-mail. Oto jak wyglądałyby kolejne kroki postępowania w przypadku korzystania z podejścia TDD.
Uwaga! W artykule będę korzystał z matcherów biblioteki Hamcrest.
Rozpocznijmy od utworzenia klasy testowej oraz pierwszej metody, której zadaniem będzie sprawdzenie, czy nowo utworzone konto nie jest aktywne:
class AccountTest { @Test void newlyCreatedAccountShouldNotBeActive() { //given Account account = new Account(); } }
Test staje się czerwony, gdyż brakuje klasy Account. Przechodzimy zatem do jej utworzenia. Warto nadmienić, że jedynie tworzymy klasę Account, ponieważ jest to minimalna ilość kodu potrzebnego do zaliczenia testu. Uruchamiamy test i udaje się go nam zaliczyć. Sprawdzamy czy istnieje możliwość refaktoryzacji. Jeśli nie – wracamy do dalszej rozbudowy testu:
@Test void newlyCreatedAccountShouldNotBeActive() { //given Account account = new Account(); //then assertThat(account.isActive(), equalTo(false)); }
Test ponownie jest czerwony ze względu na brak implementacji metody isActive(). Przejdźmy zatem do jej utworzenia:
public class Account { public boolean isActive() { return true; } }
Test skompiluje się, aczkolwiek będzie czerwony. Należy zatem dodać do klasy Account logikę tak, aby test zakończył się powodzeniem:
public class Account { private boolean active; public Account() { this.active = false; } public boolean isActive() { return this.active; } }
Wynik testu jest pozytywny. Można zakończyć kolejny cykl, gdyż refaktoryzacja nie jest ponownie potrzebna. Zakończyliśmy tworzenie pierwszej metody testowej i klasy w oparciu o podejście TDD.
Stwórzmy zatem drugą klasę testową, której zadaniem będzie sprawdzenie, czy konto jest aktywne po aktywacji:
@Test void accountShouldBeActiveAfterActivation() { //given Account account = new Account(); //when account.activate(); }
Test jest czerwony, gdyż w klasie Account brakuje metody activate(). Tworzymy zatem jedynie sygnaturę klasy, dzięki czemu test przechodzi. W obecnym momencie można pokusić się o mały refactoring. Zamiast tworzyć nowe instancje klasy Account w każdym z testów, można utworzyć ją raz jako pole klasy testowej:
class AccountTest { Account account = new Account(); @Test void newlyCreatedAccountShouldNotBeActive() { //then assertThat(account.isActive(), equalTo(false)); } @Test void accountShouldBeActiveAfterActivation() { //when account.activate(); } }
Wracamy do dalszej rozbudowy testu:
@Test void accountShouldBeActiveAfterActivation() { //when account.activate(); //then assertThat(account.isActive(),is(true)); }
Test jest czerwony, gdyż asercja nie jest spełniona. Wracamy do klasy Account, by dodać nieco logiki to metody activate():
public void activate() { this.active = true; }
Faza refaktoryzacji zostaje pominięta, więc drugi test także możemy uznać za skończony.
Doskonale zdaje sobie sprawę, że przedstawiony przykład może wydawać się trywialny, aczkolwiek ma za zadanie jedynie skupić się na toku postępowania w TDD.
Podsumowanie
Na pierwszy rzut oka stosowanie TDD może wydawać się nieopłacalne ze względu na zwiększony nakład pracy, a więc i czas. Istnieje jednak znaczna ilość zalet, która wynagradza włożony w pisanie dodatkowego kodu trud, np.:
- zmniejszenie kosztu wyłapywania błędu, ze względu na jego szybsze wykrycie
- stabilność aplikacji
- bardziej przemyślany kod
- szybsze wdrażanie zmian
Mam nadzieje, że udało mi się przedstawić czym jest metodyka TDD i jak wielką wartość niesie ze sobą bazująca na niej produkcja oprogramowania. Ważne jest, by przed rozpoczęciem projektu podjąć świadomą decyzję o zastosowaniu TDD na podstawie wszystkich czynników, nie patrząc jedynie dodatkowy czas i koszt poświęcony na pisanie testów.
Cały kod z wpisu znajduje się na moim Githubie.
certainly like your web-site but you need to take a look at the spelling on quite a few of your posts.
Several of them are rife with
spelling issues and I find
it very bothersome to tell the reality nevertheless I will definitely come again again.
Hello Elizabeth,
thanks for your feedback. It’s really great to have you here and to hear that you like the content!
My website, including blog posts, was originally written in Polish and because of a lack of time – I used a WordPress plugin called PolyLang to translate it automatically to English. I’m aware of several spelling issues and I’ll try to correct them when I find some spare time.
Thanks for understanding. Regards.
I am really loving the theme/design of your weblog.
Do you ever run into any browser compatibility
issues?
A handful of my blog
visitors have complained about my site not
working correctly in Explorer but looks great in Firefox.
Do you have any suggestions to help fix this issue?
Hello Dean,
it’s cool to hear that you like my website.
To be honest, I’ve never heard any complaints about the theme.
I’m not surprised the visitors who complied about yours used such an excellent tool as Internet Explorer haha.
Maybe you just use a theme that doesn’t support old versions of IE?
Witam,
czy mógłbys polecić materiały do nauki TDD? kursy? książki?
Hej,
ze swojej strony polecam książkę „Growing Object-Oriented Software Guided by Tests” Steve’a Freemana i Nat Pryce. Przykłady w książce są napisane w Javie, ale sama wiedza jest uniwersalna dla każdego języka programowania.