Darmowy fragment publikacji:
Tytuł oryginału: Test-Driven Java Development
Tłumaczenie: Tomasz Walczak
ISBN: 978-83-283-2341-4
Copyright © 2015 Packt Publishing
First published in the English language under the title
„Test-Driven Java Development — (9781783987429)”.
Polish edition copyright © 2015 by Helion SA. All rights reserved.
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means,
electronic or mechanical, including photocopying, recording or by any information storage retrieval system,
without permission from the Publisher.
Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej
publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną,
fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje
naruszenie praw autorskich niniejszej publikacji.
Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich
właścicieli.
Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były
kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane
z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie
ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji
zawartych w książce.
Wydawnictwo HELION
ul. Kościuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (księgarnia internetowa, katalog książek)
Pliki z przykładami omawianymi w książce można znaleźć pod adresem:
ftp://ftp.helion.pl/przyklady/tddpro.zip
Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl/user/opinie/tddpro
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
Printed in Poland.
• Kup książkę
• Poleć książkę
• Oceń książkę
• Księgarnia internetowa
• Lubię to! » Nasza społeczność
Spis tre(cid:258)ci
O autorach
O recenzentach
Przedmowa
Rozdzia(cid:239) 1. Dlaczego powiniene(cid:258) zainteresowa(cid:202) si(cid:218)
programowaniem sterowanym testami?
Dlaczego TDD?
Wprowadzenie do TDD
Czerwone, zielone, refaktoryzacja
Liczy si(cid:218) szybko(cid:258)(cid:202)
To nie testy s(cid:200) najwa(cid:285)niejsze
Przeprowadzanie testów
Testy funkcjonalne
Testy strukturalne
Ró(cid:285)nica mi(cid:218)dzy sprawdzaniem jako(cid:258)ci a zapewnianiem jako(cid:258)ci
Lepsze testy
Symulowanie dzia(cid:239)a(cid:241)
Wykonywalna dokumentacja
Brak konieczno(cid:258)ci debugowania
Podsumowanie
Rozdzia(cid:239) 2. Narz(cid:218)dzia, platformy i (cid:258)rodowiska
System Git
Maszyny wirtualne
Vagrant
Docker
Narz(cid:218)dzia do budowania kodu
(cid:165)rodowisko IDE
Przyk(cid:239)adowy projekt ze (cid:258)rodowiska IDEA
9
11
13
17
18
19
20
21
21
22
22
23
24
24
25
25
27
28
29
30
30
30
33
34
36
36
Poleć książkęKup książkę
Spis tre(cid:286)ci
Platformy do przeprowadzania testów jednostkowych
JUnit
TestNG
Hamcrest i AssertJ
Hamcrest
AssertJ
Narz(cid:218)dzia do okre(cid:258)lania pokrycia kodu testami
JaCoCo
Platformy do tworzenia zast(cid:218)pników
Mockito
EasyMock
Dodatkowe mo(cid:285)liwo(cid:258)ci atrap
Testowanie interfejsu u(cid:285)ytkownika
Platformy do testowania stron WWW
Selenium
Selenide
Programowanie sterowane zachowaniami
JBehave
Cucumber
Podsumowanie
Rozdzia(cid:239) 3. „Czerwone, zielone, refaktoryzacja”
— od pora(cid:285)ki, przez sukces, do doskona(cid:239)o(cid:258)ci
Przygotowywanie (cid:258)rodowiska z systemem Gradle i narz(cid:218)dziem JUnit
Tworzenie w (cid:258)rodowisku IntelliJ IDEA projektu wykorzystuj(cid:200)cego system Gradle i Jav(cid:218)
„Czerwone, zielone, refaktoryzacja”
Napisz test
Uruchom wszystkie testy i upewnij si(cid:218), (cid:285)e ostatni ko(cid:241)czy si(cid:218) niepowodzeniem
Napisz kod rozwi(cid:200)zania
Wykonaj wszystkie testy
Przeprowad(cid:283) refaktoryzacj(cid:218)
Powtórz ca(cid:239)y cykl
Wymagania dotycz(cid:200)ce programu do gry w kó(cid:239)ko i krzy(cid:285)yk
Pisanie programu do gry w kó(cid:239)ko i krzy(cid:285)yk
Wymaganie nr 1
Wymaganie nr 2
Wymaganie nr 3
Wymaganie nr 4
Pokrycie kodu testami
Dodatkowe (cid:202)wiczenia
Podsumowanie
4
36
38
40
42
42
44
44
45
46
48
50
51
52
52
52
54
55
56
58
60
61
62
62
65
65
66
66
66
67
67
67
68
68
74
77
83
85
86
86
Poleć książkęKup książkę
Spis tre(cid:286)ci
Rozdzia(cid:239) 4. Testy jednostkowe. Koncentrowanie si(cid:218) na wykonywanym zadaniu,
a nie na tym, co ju(cid:285) zosta(cid:239)o zrobione
Testy jednostkowe
Czym s(cid:200) testy jednostkowe?
Po co stosowa(cid:202) testy jednostkowe?
Refaktoryzacja kodu
Dlaczego nie ograniczy(cid:202) si(cid:218) do stosowania samych testów jednostkowych?
Testy jednostkowe w TDD
Platforma TestNG
Adnotacja @Test
Adnotacje @BeforeSuit, @BeforeTest, @BeforeGroups, @AfterGroups,
@AfterTest i @AfterSuit
Adnotacje @BeforeClass i @AfterClass
Adnotacje @BeforeMethod i @AfterMethod
Argument w adnotacji @Test(enable = false)
Argument w adnotacji @Test(expectedExceptions = NazwaKlasy.class)
Podsumowanie porównania platform TestNG i JUnit
Wymagania dotycz(cid:200)ce zdalnie sterowanego statku
Pisanie kodu do zdalnego sterowania statkiem
Przygotowywanie projektu
Klasy pomocnicze
Wymaganie nr 1
Wymaganie nr 2
Wymaganie nr 3
Wymaganie nr 4
Wymaganie nr 5
Wymaganie nr 6
Podsumowanie
Rozdzia(cid:239) 5. Projekt. Je(cid:258)li czego(cid:258) nie da si(cid:218) przetestowa(cid:202), projekt jest nieprawid(cid:239)owy
Dlaczego projekt ma znaczenie?
Zasady projektowe
Czwórki
Wymagania
Testowanie ostatniej wersji programu do gry Czwórki
Wymaganie nr 1
Wymaganie nr 2
Wymaganie nr 3
Wymaganie nr 4
Wymaganie nr 5
Wymaganie nr 6
Wymaganie nr 7
Wymaganie nr 8
89
90
90
91
91
91
93
94
94
95
95
95
96
96
96
97
97
97
99
100
103
105
106
109
113
113
115
116
116
118
119
119
120
121
121
122
124
124
125
126
5
Poleć książkęKup książkę
Spis tre(cid:286)ci
Program do gry w Czwórki napisany za pomoc(cid:200) TDD
Hamcrest
Wymaganie nr 1
Wymaganie nr 2
Wymaganie nr 3
Wymaganie nr 4
Wymaganie nr 5
Wymaganie nr 6
Wymaganie nr 7
Wymaganie nr 8
Podsumowanie
Rozdzia(cid:239) 6. Eliminowanie zewn(cid:218)trznych zale(cid:285)no(cid:258)ci za pomoc(cid:200) atrap
Tworzenie zast(cid:218)pników
Po co tworzy(cid:202) atrapy?
Terminologia
Obiekty pe(cid:239)ni(cid:200)ce funkcj(cid:218) atrap
Platforma Mockito
Wymagania dotycz(cid:200)ce drugiej wersji programu do gry w kó(cid:239)ko i krzy(cid:285)yk
Rozwijanie drugiej wersji programu do gry w kó(cid:239)ko i krzy(cid:285)yk
Wymaganie nr 1
Wymaganie nr 2
Testy integracyjne
Oddzielanie testów od siebie
Test integracyjny
Podsumowanie
Rozdzia(cid:239) 7. Programowanie sterowane zachowaniami
— wspó(cid:239)praca w ramach ca(cid:239)ego zespo(cid:239)u
Ró(cid:285)ne specyfikacje
Dokumentacja
Dokumentacja dla programistów
Dokumentacja dla nieprogramistów
Programowanie sterowane zachowaniami
Narracja
Scenariusze
Historia BDD dotycz(cid:200)ca ksi(cid:218)garni
JBehave
Klasa Runner dla platformy JBehave
Niegotowe kroki
Selenium i Selenide
Kroki w platformie JBehave
Ostateczne sprawdzanie poprawno(cid:258)ci
Podsumowanie
6
127
128
128
129
132
133
135
135
137
138
140
141
142
143
144
144
145
146
146
147
158
164
165
166
168
169
170
170
171
172
173
173
175
176
179
179
181
183
184
190
191
Poleć książkęKup książkę
Spis tre(cid:286)ci
Rozdzia(cid:239) 8. Refaktoryzacja zastanego kodu w celu „odm(cid:239)odzenia” go
Zastany kod
Przyk(cid:239)adowy zastany kod
(cid:109)wiczenie kata
Kata dotycz(cid:200)ce zastanego kodu
Opis
Komentarze techniczne
Dodawanie nowych funkcji
Testy funkcjonalne i testy badawcze
Wst(cid:218)pne analizy
Stosowanie algorytmu modyfikowania zastanego kodu
Wyodr(cid:218)bnianie i przes(cid:239)anianie wywo(cid:239)ania
Eliminowanie nadu(cid:285)ywania typów podstawowych
w przypadku statusu zwracanego jako warto(cid:258)(cid:202) typu int
Podsumowanie
Rozdzia(cid:239) 9. Prze(cid:239)(cid:200)czniki funkcji — wdra(cid:285)anie cz(cid:218)(cid:258)ciowo uko(cid:241)czonych funkcji
w (cid:258)rodowisku produkcyjnym
Ci(cid:200)g(cid:239)a integracja, ci(cid:200)g(cid:239)e dostarczanie i ci(cid:200)g(cid:239)e wdra(cid:285)anie
Prze(cid:239)(cid:200)czniki funkcji
Przyk(cid:239)ad zastosowania prze(cid:239)(cid:200)cznika funkcji
Pisanie kodu us(cid:239)ugi wyznaczaj(cid:200)cej liczby Fibonacciego
Korzystanie z silnika obs(cid:239)ugi szablonów
Podsumowanie
Rozdzia(cid:239) 10. (cid:146)(cid:200)czenie wszystkich informacji
TDD w pigu(cid:239)ce
Najlepsze praktyki
Konwencje nazewnicze
Procesy
Praktyki zwi(cid:200)zane z pisaniem kodu
Narz(cid:218)dzia
To tylko pocz(cid:200)tek
To nie musi by(cid:202) koniec
Skorowidz
193
194
194
204
204
205
205
205
205
206
210
216
220
223
225
226
228
229
233
236
240
241
241
243
243
245
247
251
252
252
253
7
Poleć książkęKup książkęPoleć książkęKup książkę4
Testy jednostkowe.
Koncentrowanie si(cid:218)
na wykonywanym
zadaniu, a nie na tym,
co ju(cid:285) zosta(cid:239)o zrobione
„Je(cid:258)li chcesz utworzy(cid:202) co(cid:258) wyj(cid:200)tkowego, twój umys(cid:239) musi by(cid:202)
nieustannie skoncentrowany na najdrobniejszych szczegó(cid:239)ach”.
— Giorgio Armani
Zgodnie z wcze(cid:258)niejsz(cid:200) obietnic(cid:200) w ka(cid:285)dym rozdziale opisana zostanie inna platforma do
testowania kodu napisanego w Javie. Ten rozdzia(cid:239) nie jest pod tym wzgl(cid:218)dem wyj(cid:200)tkiem.
Do budowania specyfikacji pos(cid:239)u(cid:285)y tu platforma TestNG.
W poprzednim rozdziale zastosowano procedur(cid:218) czerwone, zielone, refaktoryzacja. Wykorzy-
stano testy jednostkowe bez dok(cid:239)adnego wyt(cid:239)umaczenia, jak te testy dzia(cid:239)aj(cid:200) w kontek(cid:258)cie TDD.
Tutaj na podstawie informacji z poprzedniego rozdzia(cid:239)u szczegó(cid:239)owo wyja(cid:258)niono, czym s(cid:200) testy
jednostkowe i jakie jest ich miejsce w procesie budowania oprogramowania za pomoc(cid:200) TDD.
Poleć książkęKup książkęTDD. Programowanie w Javie sterowane testami
Celem tego rozdzia(cid:239)u jest pokazanie, jak skoncentrowa(cid:202) si(cid:218) na obecnie rozwijanej jednostce
i jak ignorowa(cid:202) lub izolowa(cid:202) wcze(cid:258)niej uko(cid:241)czone elementy.
Gdy ju(cid:285) opanujesz wiedz(cid:218) na temat platformy TestNG i testów jednostkowych, przejdziesz
bezpo(cid:258)rednio do wymaga(cid:241) zwi(cid:200)zanych z nast(cid:218)pn(cid:200) aplikacj(cid:200) i przyst(cid:200)pisz do pisania kodu.
Oto zagadnienia omówione w tym rozdziale:
(cid:81) testy jednostkowe,
(cid:81) testy jednostkowe w TDD,
(cid:81) platforma TestNG,
(cid:81) wymagania dotycz(cid:200)ce zdalnie sterowanego statku,
(cid:81) pisanie kodu zdalnie sterowanego statku,
(cid:81) podsumowanie.
Testy jednostkowe
Cz(cid:218)ste testy r(cid:218)czne s(cid:200) akceptowalne tylko w trakcie pracy nad najmniejszymi systemami.
Jedyny sposób na zast(cid:200)pienie testów r(cid:218)cznych to zastosowanie testów automatycznych.
S(cid:200) one jedyn(cid:200) skuteczn(cid:200) metod(cid:200) pozwalaj(cid:200)c(cid:200) skróci(cid:202) czas i zmniejszy(cid:202) koszty budowania,
wdra(cid:285)ania i konserwowania aplikacji. Je(cid:258)li chcesz móc efektywnie zarz(cid:200)dza(cid:202) aplikacjami, nie-
zwykle istotne jest, by kod rozwi(cid:200)zania i kod testów by(cid:239)y jak najprostsze. Prostota jest jedn(cid:200)
z podstawowych warto(cid:258)ci programowania ekstremalnego (ang. eXtreme Programming — XP;
http://www.extremeprogramming.org/rules/simple.html) oraz bardzo wa(cid:285)nym aspektem TDD
i programowania w ogóle. Najcz(cid:218)(cid:258)ciej prostot(cid:218) zapewnia si(cid:218) dzi(cid:218)ki podzia(cid:239)owi rozwi(cid:200)zania na
ma(cid:239)e jednostki. W Javie tymi jednostkami s(cid:200) metody. Poniewa(cid:285) s(cid:200) one najmniejsze, pozwalaj(cid:200)
najszybciej uzyska(cid:202) informacje zwrotne. Dlatego to w(cid:239)a(cid:258)nie metodom po(cid:258)wi(cid:218)ca si(cid:218) najwi(cid:218)cej
czasu w trakcie my(cid:258)lenia o rozwi(cid:200)zaniu i pracy nad nim. Odpowiednikiem metod z kodu roz-
wi(cid:200)zania s(cid:200) testy jednostkowe, które powinny stanowi(cid:202) najwi(cid:218)kszy procent wszystkich testów.
Czym s(cid:200) testy jednostkowe?
Testy jednostkowe to technika wymuszaj(cid:200)ca testowanie ma(cid:239)ych, pojedynczych i odizolowanych
jednostek kodu. Tymi jednostkami s(cid:200) zwykle metody, cho(cid:202) czasem u(cid:285)ywane s(cid:200) klasy, a nawet
ca(cid:239)e aplikacje. Aby napisa(cid:202) test jednostkowy, badany kod trzeba odizolowa(cid:202) od reszty aplikacji.
Najlepiej, gdy izolacja ta jest wbudowana w kod lub gdy mo(cid:285)na j(cid:200) osi(cid:200)gn(cid:200)(cid:202) za pomoc(cid:200) atrap
(wi(cid:218)cej o atrapach dowiesz si(cid:218) z rozdzia(cid:239)u 6., „Eliminowanie zewn(cid:218)trznych zale(cid:285)no(cid:258)ci za po-
moc(cid:200) atrap”). Je(cid:258)li testy jednostkowe sprawdzanej metody wykraczaj(cid:200) poza granice danej
jednostki, staj(cid:200) si(cid:218) testami integracyjnymi. W takich testach trudniej jest ustali(cid:202), które frag-
menty kodu s(cid:200) sprawdzane. Gdy test ko(cid:241)czy si(cid:218) niepowodzeniem, mo(cid:285)liwy zasi(cid:218)g wyst(cid:200)pienia
usterki ro(cid:258)nie, przez co znalezienie (cid:283)ród(cid:239)a problemu staje si(cid:218) trudniejsze.
90
Poleć książkęKup książkęRozdzia(cid:225) 4. • Testy jednostkowe
Po co stosowa(cid:202) testy jednostkowe?
Standardowe pytanie, zadawane zw(cid:239)aszcza w firmach, gdzie wykorzystuje si(cid:218) g(cid:239)ównie testy
r(cid:218)czne, brzmi: „Dlaczego powinni(cid:258)my stosowa(cid:202) testy jednostkowe zamiast testów funkcjonalnych
lub integracyjnych?”. Jest to b(cid:239)(cid:218)dnie postawione pytanie. Testy jednostkowe nie zast(cid:218)puj(cid:200) testów
innego rodzaju, a jedynie pozwalaj(cid:200) ograniczy(cid:202) zakres pozosta(cid:239)ych testów. Testy jednostkowe
z natury pisze si(cid:218) (cid:239)atwiej i szybciej ni(cid:285) testy innego typu. Pozwala to ograniczy(cid:202) koszty i szybciej
wprowadzi(cid:202) produkt na rynek. Poniewa(cid:285) takie testy mo(cid:285)na szybko pisa(cid:202) i uruchamia(cid:202), znacznie
szybciej wykrywane s(cid:200) te(cid:285) problemy. Im szybciej wykryjesz problem, tym ta(cid:241)sze b(cid:218)dzie jego
rozwi(cid:200)zanie. B(cid:239)(cid:200)d wykryty kilka minut po jego pope(cid:239)nieniu jest znacznie (cid:239)atwiejszy do na-
prawienia ni(cid:285) ta sama usterka znaleziona po kilku dniach, tygodniach czy miesi(cid:200)cach.
Refaktoryzacja kodu
Refaktoryzacja kodu to proces modyfikowania struktury istniej(cid:200)cego kodu bez zmiany jego
dzia(cid:239)ania. Celem refaktoryzacji jest ulepszenie ju(cid:285) napisanego kodu. Poprawki mo(cid:285)na wpro-
wadza(cid:202) z wielu ró(cid:285)nych powodów. Mo(cid:285)liwe, (cid:285)e chcesz zwi(cid:218)kszy(cid:202) czytelno(cid:258)(cid:202) kodu, zmniej-
szy(cid:202) jego z(cid:239)o(cid:285)ono(cid:258)(cid:202), u(cid:239)atwi(cid:202) konserwacj(cid:218), ograniczy(cid:202) koszty rozbudowywania rozwi(cid:200)zania
i tak dalej. Niezale(cid:285)nie od przyczyn refaktoryzacji g(cid:239)ównym jej celem zawsze jest ulepszenie
kodu. Skutkiem realizacji tego celu jest zmniejszenie d(cid:239)ugu technicznego, co pozwala ograni-
czy(cid:202) ilo(cid:258)(cid:202) pracy, któr(cid:200) trzeba wykona(cid:202) z powodu nieoptymalnego projektu i kodu lub niedo-
skona(cid:239)ej architektury.
Refaktoryzacja zwykle polega na wprowadzaniu zestawu drobnych zmian bez modyfikowania
po(cid:285)(cid:200)danego dzia(cid:239)ania kodu. Ograniczenie zakresu zmian w trakcie refaktoryzacji pozwala za-
chowa(cid:202) pewno(cid:258)(cid:202), (cid:285)e poprawki nie naruszy(cid:239)y dzia(cid:239)ania istniej(cid:200)cych funkcji. Jedyny sposób na
uzyskanie tej pewno(cid:258)ci polega na zastosowaniu zautomatyzowanych testów.
Jedn(cid:200) z istotnych zalet testów jednostkowych jest to, (cid:285)e s(cid:200) najlepszym narz(cid:218)dziem wspoma-
gaj(cid:200)cym przeprowadzanie refaktoryzacji. Refaktoryzacja jest zbyt ryzykowna, je(cid:258)li nie istniej(cid:200)
zautomatyzowane testy potwierdzaj(cid:200)ce, (cid:285)e aplikacja wci(cid:200)(cid:285) dzia(cid:239)a zgodnie z oczekiwaniami.
Cho(cid:202) do zapewnienia pokrycia kodu testami potrzebnego w trakcie refaktoryzacji mo(cid:285)na wy-
korzysta(cid:202) testy dowolnego typu, zwykle tylko testy jednostkowe pozwalaj(cid:200) zapewni(cid:202) po(cid:285)(cid:200)dany
poziom szczegó(cid:239)owo(cid:258)ci.
Dlaczego nie ograniczy(cid:202) si(cid:218)
do stosowania samych testów jednostkowych?
Mo(cid:285)liwe, (cid:285)e zastanawiasz si(cid:218), czy testy jednostkowe mog(cid:200) zaspokoi(cid:202) wszystkie Twoje potrzeby
zwi(cid:200)zane z testami. Niestety, nie mog(cid:200). Cho(cid:202) testy jednostkowe zwykle pozwalaj(cid:200) uwzgl(cid:218)dni(cid:202)
wi(cid:218)kszo(cid:258)(cid:202) takich potrzeb, testy funkcjonalne i integracyjne te(cid:285) powinny by(cid:202) nieod(cid:239)(cid:200)czn(cid:200) cz(cid:218)(cid:258)ci(cid:200)
Twojego zestawu narz(cid:218)dzi.
91
Poleć książkęKup książkęTDD. Programowanie w Javie sterowane testami
Inne typy testów s(cid:200) omówione szczegó(cid:239)owo w dalszych rozdzia(cid:239)ach. Na razie warto zwróci(cid:202)
uwag(cid:218) na kilka wa(cid:285)nych ró(cid:285)nic mi(cid:218)dzy poszczególnymi rodzajami testów.
(cid:81) Testy jednostkowe s(cid:239)u(cid:285)(cid:200) do sprawdzania niewielkich jednostek funkcyjnych.
W Javie tymi jednostkami s(cid:200) metody. Wszystkie zewn(cid:218)trzne zale(cid:285)no(cid:258)ci zwi(cid:200)zane
z wywo(cid:239)aniami innych klas, metod lub baz danych powinny by(cid:202) obs(cid:239)ugiwane
w pami(cid:218)ci za pomoc(cid:200) atrap, namiastek, „szpiegów” oraz zast(cid:218)pników typu fake
i dummy. Gerard Meszaros wymy(cid:258)li(cid:239) ogólne okre(cid:258)lenie „dublery u(cid:285)ywane w czasie
testów” (ang. test doubles), które obejmuje wszystkie wymienione techniki
(zobacz opis na stronie http://en.wikipedia.org/wiki/Test_double).
Testy jednostkowe s(cid:200) proste, (cid:239)atwo si(cid:218) je pisze i szybko wykonuje. Zwykle stanowi(cid:200)
najwi(cid:218)ksz(cid:200) cz(cid:218)(cid:258)(cid:202) w zestawie testów.
(cid:81) Testy funkcjonalne i akceptacyjne s(cid:239)u(cid:285)(cid:200) do sprawdzania, czy rozwijana aplikacja dzia(cid:239)a
poprawnie jako ca(cid:239)o(cid:258)(cid:202). Cho(cid:202) te dwa typy testów ró(cid:285)ni(cid:200) si(cid:218) od siebie, u(cid:285)ywa si(cid:218) ich
w tym samym celu. W odró(cid:285)nieniu od testów jednostkowych, które sprawdzaj(cid:200)
wewn(cid:218)trzn(cid:200) jako(cid:258)(cid:202) kodu, testy funkcjonalne i akceptacyjne maj(cid:200) zapewnia(cid:202), (cid:285)e system
pracuje prawid(cid:239)owo z perspektywy klienta lub u(cid:285)ytkownika. Zazwyczaj liczba tych
testów jest mniejsza ni(cid:285) liczba testów jednostkowych. Wynika to z du(cid:285)ego nak(cid:239)adu kosztów
i wysi(cid:239)ku, jaki trzeba w(cid:239)o(cid:285)y(cid:202) w pisanie testów tych dwóch typów i ich wykonywanie.
(cid:81) Testy integracyjne maj(cid:200) sprawdza(cid:202), czy odr(cid:218)bne jednostki, modu(cid:239)y, aplikacje, a nawet
ca(cid:239)e systemy s(cid:200) prawid(cid:239)owo zintegrowane ze sob(cid:200). Za(cid:239)ó(cid:285)my, (cid:285)e korzystasz z frontonu,
który na zapleczu u(cid:285)ywa interfejsu API, a ten z kolei komunikuje si(cid:218) z baz(cid:200) danych.
Testy integracyjne pozwol(cid:200) sprawdzi(cid:202), czy te trzy odr(cid:218)bne komponenty systemu s(cid:200)
zintegrowane ze sob(cid:200) i mog(cid:200) si(cid:218) mi(cid:218)dzy sob(cid:200) komunikowa(cid:202). Poniewa(cid:285) wiadomo,
(cid:285)e jednostki dzia(cid:239)aj(cid:200) poprawnie i (cid:285)e wszystkie testy funkcjonalne oraz akceptacyjne
zako(cid:241)czy(cid:239)y si(cid:218) powodzeniem, testy integracyjne zwykle stanowi(cid:200) najmniejsz(cid:200) grup(cid:218)
spo(cid:258)ród trzech omawianych kategorii testów. Wynika to z tego, (cid:285)e testy integracyjne
maj(cid:200) tylko zapewnia(cid:202), (cid:285)e wszystkie elementy poprawnie ze sob(cid:200) wspó(cid:239)pracuj(cid:200).
Widoczna na rysunku 4.1 piramida testów pokazuje, (cid:285)e testów jednostkowych powinno by(cid:202)
znacznie wi(cid:218)cej ni(cid:285) testów z wy(cid:285)szych poziomów (testów interfejsu u(cid:285)ytkownika, testów inte-
gracyjnych i tak dalej). Z czego to wynika? Testy jednostkowe s(cid:200) znacznie ta(cid:241)sze do pisania,
szybsze do uruchamiania i ponadto zapewniaj(cid:200) wy(cid:285)sze pokrycie kodu. Pomy(cid:258)l na przyk(cid:239)ad
o funkcji rejestrowania si(cid:218) u(cid:285)ytkownika. Nale(cid:285)y sprawdzi(cid:202), co si(cid:218) stanie, gdy pole na nazw(cid:218)
u(cid:285)ytkownika jest puste, gdy pole na has(cid:239)o jest puste, gdy nazwa u(cid:285)ytkownika lub has(cid:239)o ma z(cid:239)y
format, gdy dany u(cid:285)ytkownik ju(cid:285) istnieje i tak dalej. Dla tej jednej funkcji mo(cid:285)na napisa(cid:202)
dziesi(cid:200)tki, a nawet setki testów. Pisanie i uruchamianie takich testów z poziomu interfejsu
u(cid:285)ytkownika bywa bardzo kosztowne. Budowanie tego rodzaju testów jest czasoch(cid:239)onne, a ich
wykonywanie zajmuje du(cid:285)o czasu. Natomiast przeprowadzenie testu jednostkowego z u(cid:285)yciem
metody, która sprawdza poprawno(cid:258)(cid:202) omawianej funkcji, wygl(cid:200)da inaczej. Test jest wtedy prosty,
dzi(cid:218)ki czemu mo(cid:285)na go szybko napisa(cid:202) i uruchomi(cid:202). Je(cid:258)li wszystkie sytuacje s(cid:200) uwzgl(cid:218)dnione
w testach jednostkowych, wystarczy wykona(cid:202) jeden test integracyjny, sprawdzaj(cid:200)cy, czy inter-
fejs u(cid:285)ytkownika wywo(cid:239)uje na zapleczu odpowiedni(cid:200) metod(cid:218). Je(cid:285)eli j(cid:200) wywo(cid:239)uje, szczegó(cid:239)y jej
dzia(cid:239)ania w kontek(cid:258)cie integracji nie maj(cid:200) znaczenia, poniewa(cid:285) wiadomo, (cid:285)e wszystkie przy-
padki s(cid:200) sprawdzane na poziomie jednostkowym.
92
Poleć książkęKup książkęRozdzia(cid:225) 4. • Testy jednostkowe
Rysunek 4.1. Piramida testów — testy interfejsu u(cid:285)ytkownika (IU), integracyjne i jednostkowe
Testy jednostkowe w TDD
Jaka jest specyfika pisania testów jednostkowych w kontek(cid:258)cie TDD? Najwa(cid:285)niejszy jest tu
czas pisania testów. W tradycyjnych podej(cid:258)ciach testy jednostkowe s(cid:200) pisane po przygotowa-
niu kodu rozwi(cid:200)zania, w TDD natomiast testy s(cid:200) pisane na pocz(cid:200)tku. Kolejno(cid:258)(cid:202) prac jest wi(cid:218)c
odwrócona. Tradycyjnie (bez stosowania TDD) celem testów jednostkowych jest sprawdzanie
poprawno(cid:258)ci ju(cid:285) istniej(cid:200)cego kodu. Zgodnie z TDD to testy jednostkowe powinny sterowa(cid:202)
tworzeniem kodu i projektu. To testy powinny definiowa(cid:202) dzia(cid:239)anie mo(cid:285)liwie najmniejszych
jednostek. Testy s(cid:200) wtedy mikrowymaganiami czekaj(cid:200)cymi na spe(cid:239)nienie. Testy okre(cid:258)laj(cid:200), co
nale(cid:285)y zrobi(cid:202) w nast(cid:218)pnym kroku i kiedy kod jest gotowy. W zale(cid:285)no(cid:258)ci od typu stosowanych
testów (czy s(cid:200) to testy jednostkowe, funkcjonalne, czy integracyjne) zakres pó(cid:283)niejszych dzia(cid:239)a(cid:241)
jest inny. Gdy w TDD u(cid:285)ywane s(cid:200) testy jednostkowe, zakres prac jest najmniejszy i ogranicza
si(cid:218) do metody lub, cz(cid:218)(cid:258)ciej, jej fragmentu. Ponadto nale(cid:285)y wtedy stosowa(cid:202) si(cid:218) do pewnych
zasad projektowych, takich jak KISS (ang. keep it simple stupid, czyli „nie komplikuj, g(cid:239)upku”).
Je(cid:258)li przygotujesz proste testy o bardzo niewielkim zakresie prac, kod pozwalaj(cid:200)cy przej(cid:258)(cid:202) te
testy te(cid:285) zwykle b(cid:218)dzie prosty. Dzi(cid:218)ki wyeliminowaniu z testów zewn(cid:218)trznych zale(cid:285)no(cid:258)ci
trzeba odpowiednio zaprojektowa(cid:202) w kodzie rozwi(cid:200)zania podzia(cid:239) zada(cid:241). To tylko niektóre
z wielu przyk(cid:239)adów ilustruj(cid:200)cych, (cid:285)e TDD pomaga pisa(cid:202) kod wy(cid:285)szej jako(cid:258)ci. Tych samych
korzy(cid:258)ci nie da si(cid:218) uzyska(cid:202), stosuj(cid:200)c tylko testy jednostkowe. Bez TDD testy jednostkowe
sprawdzaj(cid:200) istniej(cid:200)cy kod i nie maj(cid:200) wp(cid:239)ywu na projekt.
Tak wi(cid:218)c g(cid:239)ównym celem testów jednostkowych w tradycyjnym podej(cid:258)ciu (bez TDD) jest
sprawdzanie poprawno(cid:258)ci gotowego kodu. Testy jednostkowe pisane na pocz(cid:200)tku, w modelu
TDD, s(cid:239)u(cid:285)(cid:200) g(cid:239)ównie do tworzenia specyfikacji i projektu. Sprawdzanie poprawno(cid:258)ci jest tylko
dodatkow(cid:200) korzy(cid:258)ci(cid:200), przy czym testy maj(cid:200) wtedy zazwyczaj wy(cid:285)sz(cid:200) jako(cid:258)(cid:202) ni(cid:285) wówczas, gdy
powstaj(cid:200) dopiero po napisaniu kodu rozwi(cid:200)zania.
93
Poleć książkęKup książkęTDD. Programowanie w Javie sterowane testami
TDD zmusza do przemy(cid:258)lenia wymaga(cid:241) i projektu, pisania przejrzystego i dzia(cid:239)aj(cid:200)cego kodu,
tworzenia wykonywalnych wymaga(cid:241) oraz bezpiecznego i cz(cid:218)stego refaktoryzowania rozwi(cid:200)-
zania. Ponadto uzyskujesz wysokie pokrycie kodu testami, co pozwala przeprowadzi(cid:202) testy
regresyjne ca(cid:239)ego kodu po wprowadzeniu w nim zmian. Efektem testów jednostkowych bez
TDD s(cid:200) tylko testy, i to cz(cid:218)sto o w(cid:200)tpliwej jako(cid:258)ci.
Platforma TestNG
JUnit i TestNG to dwie g(cid:239)ówne platformy do testowania kodu w Javie. W poprzednim roz-
dziale pisa(cid:239)e(cid:258) ju(cid:285) testy za pomoc(cid:200) platformy JUnit i, miejmy nadziej(cid:218), dobrze rozumiesz jej
dzia(cid:239)anie. A co z TestNG? Ta platforma powsta(cid:239)a w celu utworzenia ulepszonej wersji JUnit.
I rzeczywi(cid:258)cie — zawiera pewne funkcje niedost(cid:218)pne w JUnit.
W dalszych punktach znajdziesz przegl(cid:200)d ró(cid:285)nic mi(cid:218)dzy tymi dwoma platformami. Autorzy
tej ksi(cid:200)(cid:285)ki staraj(cid:200) si(cid:218) nie tylko wyja(cid:258)ni(cid:202) te ró(cid:285)nice, ale te(cid:285) oceni(cid:202) ich znaczenie w kontek(cid:258)cie
stosowania testów jednostkowych razem z TDD.
Adnotacja @Test
W platformach JUnit i TestNG adnotacja @Test jest u(cid:285)ywana do wskazywania metod uznawa-
nych za test. W JUnit ka(cid:285)da taka metoda musi mie(cid:202) adnotacj(cid:218) @Test, w TestNG natomiast
mo(cid:285)na umie(cid:258)ci(cid:202) t(cid:218) adnotacj(cid:218) na poziomie klasy. Ta adnotacja na poziomie klasy sprawia, (cid:285)e
wszystkie metody publiczne s(cid:200) uznawane za testy, chyba (cid:285)e programista doda do wybranych
metod inne modyfikatory.
@Test
public class DirectionSpec {
public void whenGetFromShortNameNThenReturnDirectionN() {
Direction direction = Direction.getFromShortName( N );
assertEquals(direction, Direction.NORTH);
}
public void whenGetFromShortNameWThenReturnDirectionW() {
Direction direction = Direction.getFromShortName( W );
assertEquals(direction, Direction.WEST);
}
}
W tym przyk(cid:239)adzie adnotacja @Test znajduje si(cid:218) nad klas(cid:200) DirectionSpec. Dlatego metody
whenGetFromShortNameNThenReturnDirectionN i whenGetFromShortNameWThenReturnDirectionW
s(cid:200) uznawane za testy. W tym samym kodzie w platformie JUnit adnotacja @Test musi si(cid:218) znaj-
dowa(cid:202) przy obu metodach.
94
Poleć książkęKup książkęRozdzia(cid:225) 4. • Testy jednostkowe
Adnotacje @BeforeSuit, @BeforeTest, @BeforeGroups,
@AfterGroups, @AfterTest i @AfterSuit
Te adnotacje nie maj(cid:200) swoich odpowiedników w platformie JUnit. Platforma TestNG pozwala
grupowa(cid:202) testy w pakiety za pomoc(cid:200) konfiguracji w formacie XML. Metody z adnotacjami
@BeforeSuit i @AfterSuit s(cid:200) uruchamiane przed testami z pakietu lub po ca(cid:239)ym pakiecie.
Metody z adnotacjami @BeforeTest i @AfterTest s(cid:200) wykonywane przed ka(cid:285)d(cid:200) metod(cid:200) testow(cid:200)
z klasy albo po ka(cid:285)dej takiej metodzie. Testy w platformie TestNG mo(cid:285)na te(cid:285) (cid:239)(cid:200)czy(cid:202) w grupy.
Adnotacje @BeforeGroup i @AfterGroup umo(cid:285)liwiaj(cid:200) uruchamianie metod przed pierwszym
testem z grupy i po wykonaniu wszystkich testów z grupy.
Cho(cid:202) te adnotacje mog(cid:200) by(cid:202) bardzo u(cid:285)yteczne, je(cid:258)li testy s(cid:200) pisane po kodzie rozwi(cid:200)zania,
w kontek(cid:258)cie TDD ich przydatno(cid:258)(cid:202) jest umiarkowana. Inaczej ni(cid:285) w tradycyjnych testach,
które cz(cid:218)sto planuje si(cid:218) i pisze w ramach odr(cid:218)bnego projektu, w TDD nale(cid:285)y dodawa(cid:202) za
ka(cid:285)dym razem jeden prosty test. Jeszcze wa(cid:285)niejsze jest to, (cid:285)e testy maj(cid:200) dzia(cid:239)a(cid:202) szybko, dla-
tego nie trzeba (cid:239)(cid:200)czy(cid:202) ich w pakiety lub grupy. Gdy testy dzia(cid:239)aj(cid:200) szybko, pomijanie których(cid:258)
z nich jest marnotrawstwem. Je(cid:258)li wszystkie testy mo(cid:285)na wykona(cid:202) na przyk(cid:239)ad w mniej ni(cid:285) 15
sekund, nie ma potrzeby uruchamia(cid:202) tylko cz(cid:218)(cid:258)ci z nich. Natomiast gdy testy dzia(cid:239)aj(cid:200) powoli,
cz(cid:218)sto oznacza to, (cid:285)e nie odizolowano zewn(cid:218)trznych zale(cid:285)no(cid:258)ci. Niezale(cid:285)nie od przyczyn
wp(cid:239)ywaj(cid:200)cych na powolne wykonywanie testów rozwi(cid:200)zanie nie powinno polega(cid:202) na wyko-
nywaniu tylko wybranych z nich, ale na naprawieniu problemu.
Ponadto testy funkcjonalne i integracyjne s(cid:200) zwykle wolniejsze i wymagaj(cid:200) rozdzielenia.
Warto rozdzieli(cid:202) je na przyk(cid:239)ad za pomoc(cid:200) instrukcji z pliku build.gradle, tak by dla ka(cid:285)dego
typu testów u(cid:285)ywane by(cid:239)o odr(cid:218)bne zadanie.
Adnotacje @BeforeClass i @AfterClass
Te adnotacje dzia(cid:239)aj(cid:200) tak samo w platformach JUnit i TestNG. Metody z tymi adnotacjami s(cid:200)
uruchamiane przed pierwszym testem i po ostatnim te(cid:258)cie z danej klasy. Jedyna ró(cid:285)nica pole-
ga na tym, (cid:285)e w platformie TestNG metody z t(cid:200) adnotacj(cid:200) nie musz(cid:200) by(cid:202) statyczne. Jest tak,
poniewa(cid:285) w obu platformach stosowane s(cid:200) odmienne podej(cid:258)cia w trakcie uruchamiania me-
tod z testami. Platforma JUnit izoluje testy i uruchamia je w odr(cid:218)bnych egzemplarzach klasy
z testami. Dlatego metody trzeba zdefiniowa(cid:202) jako statyczne, co pozwala wielokrotnie wyko-
rzysta(cid:202) je we wszystkich przebiegach testów. Platforma TestNG wykonuje wszystkie metody
testowe w kontek(cid:258)cie jednego egzemplarza klasy, metody nie musz(cid:200) by(cid:202) wi(cid:218)c statyczne.
Adnotacje @BeforeMethod i @AfterMethod
Adnotacje @BeforeMethod i @AfterMethod dzia(cid:239)aj(cid:200) tak jak ich odpowiedniki z platformy JUnit.
Metody z tymi adnotacjami s(cid:200) uruchamiane przed ka(cid:285)d(cid:200) metod(cid:200) testow(cid:200) i po ka(cid:285)dej takiej
metodzie.
95
Poleć książkęKup książkęTDD. Programowanie w Javie sterowane testami
Argument w adnotacji @Test(enable = false)
Platformy JUnit i TestNG umo(cid:285)liwiaj(cid:200) wy(cid:239)(cid:200)czenie testów. W platformie JUnit s(cid:239)u(cid:285)y do tego
odr(cid:218)bna adnotacja @Ignore, w platformie TestNG natomiast nale(cid:285)y u(cid:285)y(cid:202) argumentu logicznego
enable w adnotacji @Test. Oba te rozwi(cid:200)zania dzia(cid:239)aj(cid:200) tak samo, a ró(cid:285)nica polega tylko na zapisie.
Argument w adnotacji
@Test(expectedExceptions = NazwaKlasy.class)
W tym punkcie platforma JUnit ma przewag(cid:218). Cho(cid:202) obie platformy umo(cid:285)liwiaj(cid:200) wskazywanie
oczekiwanych wyj(cid:200)tków w ten sam sposób (przy czym w platformie JUnit s(cid:239)u(cid:285)(cid:200)cy do tego
argument nosi nazw(cid:218) expected), JUnit udost(cid:218)pnia regu(cid:239)y, które pozwalaj(cid:200) w bardziej ele-
gancki sposób testowa(cid:202) wyj(cid:200)tki. Z regu(cid:239) korzysta(cid:239)e(cid:258) ju(cid:285) w rozdziale 2., „Narz(cid:218)dzia, platformy
i (cid:258)rodowiska”.
Podsumowanie porównania platform TestNG i JUnit
Mi(cid:218)dzy omawianymi platformami wyst(cid:218)puj(cid:200) te(cid:285) liczne inne ró(cid:285)nice. By zachowa(cid:202) zwi(cid:218)z(cid:239)o(cid:258)(cid:202),
nie opisano ich wszystkich w tej ksi(cid:200)(cid:285)ce. Wi(cid:218)cej informacji znajdziesz w dokumentacji obu
narz(cid:218)dzi.
Wi(cid:218)cej informacji o platformach JUnit i TestNG znajdziesz na stronach http://junit.org/ i http://testng.org/.
Platforma TestNG udost(cid:218)pnia wi(cid:218)cej funkcji i jest bardziej zaawansowana ni(cid:285) JUnit. W tym
rozdziale b(cid:218)dziesz u(cid:285)ywa(cid:239) platformy TestNG, dzi(cid:218)ki czemu lepiej j(cid:200) poznasz. Zauwa(cid:285)ysz jed-
nak, (cid:285)e nie s(cid:200) tu stosowane (cid:285)adne z zaawansowanych funkcji tej platformy. Wynika to z tego,
(cid:285)e w TDD rzadko s(cid:200) one potrzebne w trakcie tworzenia testów jednostkowych. Testy funk-
cjonalne i integracyjne dzia(cid:239)aj(cid:200) inaczej, dlatego pozwoli(cid:239)yby lepiej zademonstrowa(cid:202) przewag(cid:218)
platformy TestNG. Istniej(cid:200) jednak narz(cid:218)dzia lepiej dostosowane do takich testów. Przekonasz
si(cid:218) o tym w dalszych rozdzia(cid:239)ach.
Której z tych platform powiniene(cid:258) u(cid:285)ywa(cid:202)? Wybór nale(cid:285)y do Ciebie. Do czasu zako(cid:241)czenia
lektury tego rozdzia(cid:239)u zdob(cid:218)dziesz praktyczne umiej(cid:218)tno(cid:258)ci pos(cid:239)ugiwania si(cid:218) zarówno platform(cid:200)
JUnit, jak i platform(cid:200) TestNG.
96
Poleć książkęKup książkęRozdzia(cid:225) 4. • Testy jednostkowe
Wymagania dotycz(cid:200)ce
zdalnie sterowanego statku
Tu wykonasz odmian(cid:218) dobrze znanego (cid:202)wiczenia Mars Rover, opublikowanego po raz pierwszy
w witrynie Dallas Hack Club (http://dallashackclub.com/rover).
Wyobra(cid:283) sobie, (cid:285)e gdzie(cid:258) po morzach p(cid:239)ywa statek. Poniewa(cid:285) (cid:285)yjemy w XXI wieku, maszyn(cid:200)
mo(cid:285)na sterowa(cid:202) zdalnie.
Zadanie polega na napisaniu programu, który pozwala sterowa(cid:202) przemieszczaniem si(cid:218) statku po morzach.
Jako (cid:285)e jest to ksi(cid:200)(cid:285)ka o TDD, a tematem tego rozdzia(cid:239)u s(cid:200) testy jednostkowe, rozwiniesz aplikacj(cid:218)
za pomoc(cid:200) TDD, koncentruj(cid:200)c si(cid:218) na testach jednostkowych. W poprzednim rozdziale pozna(cid:239)e(cid:258)
procedur(cid:218) czerwone, zielone, refaktoryzacja w teorii, a tak(cid:285)e wypróbowa(cid:239)e(cid:258) j(cid:200) w praktyce. Tu wy-
korzystasz zdobyt(cid:200) wcze(cid:258)niej wiedz(cid:218) i nauczysz si(cid:218) skutecznie stosowa(cid:202) testy jednostkowe.
Skoncentrujesz si(cid:218) na rozwijanej jednostce i dowiesz si(cid:218), jak izolowa(cid:202) i pomija(cid:202) zale(cid:285)no(cid:258)ci, które
mog(cid:200) by(cid:202) potrzebne w danej jednostce. Nast(cid:218)pnym celem jest uwzgl(cid:218)dnianie w ka(cid:285)dym kroku
tylko jednego wymagania. Dlatego prezentowane s(cid:200) tu wy(cid:239)(cid:200)cznie wymagania wysokiego poziomu.
U(cid:285)ytkownik powinien móc zdalnie sterowa(cid:202) statkiem zlokalizowanym w dowolnym miejscu (cid:258)wiata.
W celu uproszczenia Ci pracy wszystkie klasy pomocnicze zosta(cid:239)y ju(cid:285) napisane i przetestowane.
Dzi(cid:218)ki temu mo(cid:285)esz si(cid:218) skoncentrowa(cid:202) tylko na g(cid:239)ównym zadaniu, a (cid:202)wiczenie b(cid:218)dzie zwi(cid:218)z(cid:239)e.
Pisanie kodu
do zdalnego sterowania statkiem
Zacznij od zaimportowania istniej(cid:200)cego repozytorium Git.
Przygotowywanie projektu
Rozpocznij od przygotowania projektu.
1. Otwórz (cid:258)rodowisko IntelliJ IDEA. Je(cid:258)li otwarty jest istniej(cid:200)cy projekt,
wybierz opcj(cid:218) File/Close Project.
Pojawi si(cid:218) ekran podobny do ekranu widocznego na rysunku 4.2.
2. Aby zaimportowa(cid:202) projekt z repozytorium Git, wybierz opcj(cid:218) Check out from Version
Control/Git. Wpisz https://bitbucket.org/vfarcic/tdd-java-ch04-ship.git
w polu Git Repository URL i kliknij przycisk Clone (zobacz rysunek 4.3).
97
Poleć książkęKup książkęTDD. Programowanie w Javie sterowane testami
Rysunek 4.2. Pocz(cid:200)tkowy ekran (cid:258)rodowiska IntelliJ IDEA
Rysunek 4.3. Importowanie projektu z repozytorium Git
3. Gdy zobaczysz pytanie o to, czy chcesz otworzy(cid:202) projekt, wybierz opcj(cid:218) Yes.
Nast(cid:218)pnie zobaczysz pokazane na rysunku 4.4 okno dialogowe Import Project
from Gradle. Kliknij przycisk OK.
Rysunek 4.4. Otwieranie projektu
98
Poleć książkęKup książkęRozdzia(cid:225) 4. • Testy jednostkowe
4. (cid:165)rodowisko IDEA potrzebuje czasu na pobranie wszystkich zale(cid:285)no(cid:258)ci opisanych
w pliku build.gradle. Po zako(cid:241)czeniu ich wczytywania zobaczysz, (cid:285)e niektóre klasy
i powi(cid:200)zane z nimi testy s(cid:200) ju(cid:285) gotowe. Przedstawia to rysunek 4.5.
Rysunek 4.5. Struktura wczytanego projektu
Klasy pomocnicze
Wyobra(cid:283) sobie, (cid:285)e prace nad tym projektem rozpocz(cid:200)(cid:239) Twój wspó(cid:239)pracownik. Jest dobrym
programist(cid:200) i praktykiem TDD. Ufasz, (cid:285)e zapewni(cid:239) dobre pokrycie kodu testami, dlatego mo-
(cid:285)esz polega(cid:202) na jego pracy. Wspó(cid:239)pracownik nie zd(cid:200)(cid:285)y(cid:239) jednak uko(cid:241)czy(cid:202) aplikacji przed wy-
jazdem na urlop i to Ty musisz kontynuowa(cid:202) rozwijanie programu. Gotowe s(cid:200) ju(cid:285) wszystkie
klasy pomocnicze: Direction, Location, Planet i Point. Zauwa(cid:285), (cid:285)e przygotowane zosta(cid:239)y te(cid:285)
odpowiednie klasy z testami. Nazwy klas z testami utworzono przez dodanie przedrostka Spec
do nazw sprawdzanych klas (na przyk(cid:239)ad DirectionSpec). Ten przedrostek zastosowano po to,
by podkre(cid:258)li(cid:202), (cid:285)e testy nie tylko s(cid:239)u(cid:285)(cid:200) do sprawdzania poprawno(cid:258)ci kodu, ale tak(cid:285)e pe(cid:239)ni(cid:200)
funkcj(cid:218) wykonywalnej specyfikacji.
Oprócz klas pomocniczych istniej(cid:200) równie(cid:285) klasy Ship (z rozwi(cid:200)zaniem) i ShipSpec (z testami
u(cid:285)ywanymi jako specyfikacja). Wi(cid:218)kszo(cid:258)(cid:202) czasu po(cid:258)wi(cid:218)cisz na prac(cid:218) nad tymi dwoma klasami.
W klasie ShipSpec b(cid:218)dziesz pisa(cid:239) testy, a nast(cid:218)pnie dodasz kod rozwi(cid:200)zania do klasy Ship.
Kolejno(cid:258)(cid:202) prac b(cid:218)dzie wi(cid:218)c taka sama jak wcze(cid:258)niej.
99
Poleć książkęKup książkęTDD. Programowanie w Javie sterowane testami
Poniewa(cid:285) wiesz ju(cid:285), (cid:285)e testy nie tylko s(cid:239)u(cid:285)(cid:200) do sprawdzania poprawno(cid:258)ci kodu, ale te(cid:285)
s(cid:200) wykonywaln(cid:200) dokumentacj(cid:200), od tego miejsca u(cid:285)ywane b(cid:218)dzie okre(cid:258)lenie specyfikacja
zamiast test.
Za ka(cid:285)dym razem, gdy napiszesz specyfikacj(cid:218) lub odpowiadaj(cid:200)cy jej kod, uruchom polecenie
gradle test w wierszu polece(cid:241) b(cid:200)d(cid:283) w przedstawionym na rysunku 4.6 oknie Gradle projects
w (cid:258)rodowisku IDEA.
Rysunek 4.6. Okno Gradle projects
Po przygotowaniu projektu mo(cid:285)esz przej(cid:258)(cid:202) do pierwszego wymagania.
Wymaganie nr 1
Trzeba okre(cid:258)li(cid:202) bie(cid:285)(cid:200)c(cid:200) lokalizacj(cid:218) statku, by móc go przemie(cid:258)ci(cid:202). Ponadto trzeba wiedzie(cid:202),
w któr(cid:200) stron(cid:218) statek jest skierowany — na pó(cid:239)noc, na po(cid:239)udnie, na wschód czy na zachód.
Tego w(cid:239)a(cid:258)nie dotyczy pierwsze wymaganie.
Okre(cid:258)lone s(cid:200) punkt pocz(cid:200)tkowy (x, y) statku oraz jego kierunek (N — ang. north, czyli pó(cid:239)noc, S — ang.
south, czyli po(cid:239)udnie, E — ang. east, czyli wschód, lub W — ang. west, czyli zachód).
Zanim zaczniesz pracowa(cid:202) nad wymaganiem, zapoznaj si(cid:218) z dost(cid:218)pnymi klasami pomocniczymi.
Klasa Point przechowuje wspó(cid:239)rz(cid:218)dne x i y. Oto konstruktor tej klasy:
public Point(int x, int y) {
this.x = x;
this.y = y;
}
100
Poleć książkęKup książkęDost(cid:218)pna jest te(cid:285) klasa Direction enum z pokazanymi poni(cid:285)ej warto(cid:258)ciami:
Rozdzia(cid:225) 4. • Testy jednostkowe
public enum Direction {
NORTH(0, N ),
EAST(1, E ),
SOUTH(2, S ),
WEST(3, W ),
NONE(4, X );
}
Istnieje równie(cid:285) klasa Location, która wymaga przekazania obiektów obu wcze(cid:258)niej pokazanych
klas jako argumentów konstruktora.
public Location(Point point, Direction direction) {
this.point = point;
this.direction = direction;
}
Dzi(cid:218)ki tym klasom nie powiniene(cid:258) mie(cid:202) wi(cid:218)kszych problemów z napisaniem testu dla pierw-
szego wymagania. Proces ten powinien wygl(cid:200)da(cid:202) tak samo jak w poprzednim rozdziale.
Spróbuj samodzielnie napisa(cid:202) specyfikacje. Gdy je uko(cid:241)czysz, porównaj swój kod ze spe-
cyfikacj(cid:200) z tej ksi(cid:200)(cid:285)ki. To samo zrób w trakcie pracy nad kodem rozwi(cid:200)zania powi(cid:200)zanym
ze specyfikacj(cid:200). Spróbuj napisa(cid:202) go samodzielnie, a nast(cid:218)pnie porównaj go z proponowanym
rozwi(cid:200)zaniem z tej ksi(cid:200)(cid:285)ki.
Specyfikacja
Oto mo(cid:285)liwa specyfikacja dla pierwszego wymagania:
@Test
public class ShipSpec {
public void whenInstantiatedThenLocationIsSet() {
Location location = new Location(
new Point(21, 13), Direction.NORTH);
Ship ship = new Ship(location);
assertEquals(ship.getLocation(), location);
}
}
To by(cid:239)o (cid:239)atwe. Wystarczy sprawdzi(cid:202), czy obiekt typu Location przekazany do konstruktora obiektu
typu Ship jest zachowywany i czy mo(cid:285)na uzyska(cid:202) dost(cid:218)p do lokalizacji za pomoc(cid:200) gettera location.
Adnotacja @Test
Gdy w platformie TestNG adnotacja @Test jest ustawiona na poziomie klasy, nie trzeba okre(cid:258)la(cid:202), które metody
maj(cid:200) pe(cid:239)ni(cid:202) funkcj(cid:218) testów. Wtedy wszystkie publiczne metody s(cid:200) uznawane za testy z platformy TestNG.
101
Poleć książkęKup książkęTDD. Programowanie w Javie sterowane testami
Kod rozwi(cid:200)zania
Kod rozwi(cid:200)zania powi(cid:200)zany ze specyfikacj(cid:200) jest stosunkowo (cid:239)atwy do napisania. Wystarczy
przypisa(cid:202) argument z konstruktora do zmiennej location.
public class Ship {
private final Location location;
public Location getLocation() {
return location;
}
public Ship(Location location) {
this.location = location;
}
}
Kompletny kod (cid:283)ród(cid:239)owy znajdziesz w odga(cid:239)(cid:218)zieniu req1-location w repozytorium
tdd-java-ch04-ship dost(cid:218)pnym na stronie https://bitbucket.org/vfarcic/tdd-java-ch04-ship/
branch/req01-location.
Refaktoryzacja
Wiesz, (cid:285)e obiekt typu Ship trzeba b(cid:218)dzie utworzy(cid:202) dla ka(cid:285)dej specyfikacji. Dlatego mo(cid:285)na zre-
faktoryzowa(cid:202) klas(cid:218) specyfikacji i doda(cid:202) w niej adnotacj(cid:218) @BeforeMethod. Oto nowy kod tej klasy:
@Test
public class ShipSpec {
private Ship ship;
private Location location;
@BeforeMethod
public void beforeTest() {
Location location = new Location(
new Point(21, 13), Direction.NORTH);
ship = new Ship(location);
}
public void whenInstantiatedThenLocationIsSet() {
// Location location = new Location(
// new Point(21, 13), Direction.NORTH);
// Ship ship = new Ship(location);
assertEquals(ship.getLocation(), location);
}
}
102
Poleć książkęKup książkęRozdzia(cid:225) 4. • Testy jednostkowe
Nie dodano tu (cid:285)adnych nowych operacji. Wystarczy(cid:239)o przenie(cid:258)(cid:202) fragment kodu do adnotacji
@BeforeMethod, by unikn(cid:200)(cid:202) powtórze(cid:241), które powsta(cid:239)yby w trakcie pisania dalszych specyfikacji.
Teraz przy ka(cid:285)dym uruchomieniu testu utworzony zostanie obiekt typu Ship z argumentem
location.
Wymaganie nr 2
Gdy wiesz ju(cid:285), gdzie statek si(cid:218) znajduje, spróbuj go przesun(cid:200)(cid:202). Zacznij od mo(cid:285)liwo(cid:258)ci prze-
mieszczania go do przodu i do ty(cid:239)u.
Dodaj obs(cid:239)ug(cid:218) polece(cid:241) przesuwaj(cid:200)cych statek naprzód (n) i wstecz (w).
Klasa pomocnicza Location udost(cid:218)pnia ju(cid:285) metody forward i backward obs(cid:239)uguj(cid:200)ce takie polecenia.
public boolean forward() {
...
}
Specyfikacja
Co si(cid:218) powinno sta(cid:202), gdy statek jest skierowany na pó(cid:239)noc i u(cid:285)ytkownik przesunie go do przodu?
Warto(cid:258)(cid:202) wspó(cid:239)rz(cid:218)dnej y powinna si(cid:218) zmniejszy(cid:202). Inny przyk(cid:239)ad to przesuni(cid:218)cie do przodu
statku skierowanego na wschód; w takiej sytuacji nale(cid:285)y zwi(cid:218)kszy(cid:202) warto(cid:258)(cid:202) wspó(cid:239)rz(cid:218)dnej x.
Pierwsz(cid:200) reakcj(cid:200) mo(cid:285)e by(cid:202) napisanie specyfikacji podobnych do dwóch poni(cid:285)szych:
public void givenNorthWhenMoveForwardThenYDecreases() {
ship.moveForward();
assertEquals(ship.getLocation().getPoint().getY(), 12);
}
public void givenEastWhenMoveForwardThenXIncreases() {
ship.getLocation().setDirection(Direction.EAST);
ship.moveForward();
assertEquals(ship.getLocation().getPoint().getX(), 22);
}
W tym podej(cid:258)ciu trzeba utworzy(cid:202) jeszcze przynajmniej dwie specyfikacje dotycz(cid:200)ce sytuacji,
w których statek jest skierowany na po(cid:239)udnie i na zachód.
Jednak nie tak nale(cid:285)y pisa(cid:202) testy jednostkowe. Wi(cid:218)kszo(cid:258)(cid:202) osób zaczynaj(cid:200)cych stosowanie
takich testów wpada w pu(cid:239)apk(cid:218) okre(cid:258)lania wyników ko(cid:241)cowych wymagaj(cid:200)cych znajomo(cid:258)ci
wewn(cid:218)trznych mechanizmów metod, klas i bibliotek u(cid:285)ywanych przez sprawdzan(cid:200) metod(cid:218).
To podej(cid:258)cie prowadzi do problemów na wielu poziomach.
103
Poleć książkęKup książkęTDD. Programowanie w Javie sterowane testami
Gdy w sprawdzanej jednostce korzystasz z kodu zewn(cid:218)trznego, powiniene(cid:258) pami(cid:218)ta(cid:202) o tym,
(cid:285)e — przynajmniej w tym przyk(cid:239)adzie — zewn(cid:218)trzny kod jest ju(cid:285) przetestowany. Po ka(cid:285)dej
zmianie wykonywane s(cid:200) wszystkie testy, dlatego wiadomo, (cid:285)e kod zewn(cid:218)trzny dzia(cid:239)a.
Zawsze po wprowadzeniu zmian w kodzie rozwi(cid:200)zania uruchamiaj wszystkie testy
To gwarantuje, (cid:285)e nie pojawi(cid:200) si(cid:218) nieoczekiwane efekty uboczne spowodowane zmianami w kodzie.
Za ka(cid:285)dym razem, gdy zmodyfikujesz jak(cid:200)(cid:258) cz(cid:218)(cid:258)(cid:202) kodu rozwi(cid:200)zania, przeprowad(cid:283) wszystkie testy. W ideal-
nych warunkach testy powinny dzia(cid:239)a(cid:202) szybko i by(cid:202) wykonywane lokalnie przez programist(cid:218). Po prze-
s(cid:239)aniu kodu do systemu kontroli wersji wszystkie testy nale(cid:285)y przeprowadzi(cid:202) jeszcze raz, by si(cid:218) upewni(cid:202),
(cid:285)e nie wyst(cid:200)pi(cid:239)y problemy wynikaj(cid:200)ce ze scalania kodu. Jest to wa(cid:285)ne zw(cid:239)aszcza wtedy, gdy nad kodem
pracuje kilka osób. Do pobrania kodu z repozytorium, skompilowania rozwi(cid:200)zania i wykonania testów mo(cid:285)na
wykorzysta(cid:202) narz(cid:218)dzia do obs(cid:239)ugi ci(cid:200)g(cid:239)ej integracji, takie jak Jenkins, Hudson, Travind, Bamboo lub Go-CD.
Inny problem z opisanym podej(cid:258)ciem polega na tym, (cid:285)e je(cid:258)li w zewn(cid:218)trznym kodzie pojawi(cid:200) si(cid:218)
zmiany, trzeba b(cid:218)dzie zmodyfikowa(cid:202) wiele specyfikacji. Najlepiej jest, gdy zmiany s(cid:200) niezb(cid:218)dne
tylko w specyfikacjach bezpo(cid:258)rednio powi(cid:200)zanych z modyfikowan(cid:200) jednostk(cid:200). Wyszukiwanie w in-
nych miejscach wywo(cid:239)a(cid:241) danej jednostki bywa bardzo czasoch(cid:239)onne i grozi powstawaniem b(cid:239)(cid:218)dów.
Poni(cid:285)ej pokazano znacznie (cid:239)atwiejszy, szybszy i lepszy sposób na napisanie specyfikacji dla
omawianego wymagania.
public void whenMoveForwardThenForward() {
Location expected = location.copy();
expected.forward();
ship.moveForward();
assertEquals(ship.getLocation(), expected);
}
Poniewa(cid:285) klasa Location ma ju(cid:285) metod(cid:218) forward, wystarczy si(cid:218) upewni(cid:202), (cid:285)e metoda ta zosta-
nie poprawnie wywo(cid:239)ana. Tu kod tworzy nowy obiekt expected typu Location, wywo(cid:239)uje jego
metod(cid:218) forward i porównuje lokalizacj(cid:218) obiekt expected z lokalizacj(cid:200) statku po wywo(cid:239)aniu jego
metody moveForward.
Zauwa(cid:285), (cid:285)e specyfikacje s(cid:239)u(cid:285)(cid:200) nie tylko do sprawdzania poprawno(cid:258)ci kodu. S(cid:200) równie(cid:285) u(cid:285)y-
wane jako wykonywalna dokumentacja i, co najwa(cid:285)niejsze, wspomagaj(cid:200) my(cid:258)lenie oraz pro-
jektowanie. Druga wersja specyfikacji bardziej precyzyjnie okre(cid:258)la jej przeznaczenie. W klasie
Ship nale(cid:285)y doda(cid:202) metod(cid:218) moveForward i wywo(cid:239)a(cid:202) w niej instrukcj(cid:218) location.forward.
Kod rozwi(cid:200)zania
Po przygotowaniu tak krótkiej i jasno zdefiniowanej specyfikacji napisanie kodu, który po-
zwala j(cid:200) spe(cid:239)ni(cid:202), powinno by(cid:202) (cid:239)atwe.
public boolean moveForward() {
return location.forward();
}
104
Poleć książkęKup książkęRozdzia(cid:225) 4. • Testy jednostkowe
Specyfikacja
Po utworzeniu specyfikacji dotycz(cid:200)cej ruchu do przodu i napisaniu odpowiadaj(cid:200)cego jej kodu
rozwi(cid:200)zania pora zaj(cid:200)(cid:202) si(cid:218) ruchem wstecz. Specyfikacja wygl(cid:200)da tu prawie identycznie.
public void whenMoveBackwardThenBackward() {
Location expected = location.copy();
expected.backward();
ship.moveBackward();
assertEquals(ship.getLocation(), expected);
}
Kod rozwi(cid:200)zania
Kod rozwi(cid:200)zania zwi(cid:200)zany z ruchem wstecz jest, podobnie jak kod specyfikacji, (cid:239)atwy do napisania.
public boolean moveBackward() {
return location.backward();
}
Kompletny kod (cid:283)ród(cid:239)owy dotycz(cid:200)cy tego wymagania znajdziesz w odga(cid:239)(cid:218)zieniu req02-forward-
(cid:180)backward w repozytorium tdd-java-ch04-ship dost(cid:218)pnym na stronie https://bitbucket.org/
vfarcic/tdd-java-ch04-ship/branch/req02-forward-backward.
Wymaganie nr 3
Przemieszczanie statku tylko do przodu i do ty(cid:239)u nie wystarczy. Potrzebna jest te(cid:285) mo(cid:285)liwo(cid:258)(cid:202)
sterowania statkiem i robienia zwrotów w lewo oraz w prawo.
Dodaj polecenia powoduj(cid:200)ce obrót statku w lewo (l) i prawo (p).
Po dodaniu kodu rozwi(cid:200)zania dla poprzedniego wymagania nowe zadanie nie powinno sprawi(cid:202)
Ci (cid:285)adnych trudno(cid:258)ci, poniewa(cid:285) mo(cid:285)na w nim wykorzysta(cid:202) t(cid:218) sam(cid:200) logik(cid:218). Klasa pomocnicza
Location zawiera ju(cid:285) metody turnLeft i turnRight, które wykonuj(cid:200) okre(cid:258)lone w wymaganiu
operacje. Teraz wystarczy zintegrowa(cid:202) te metody z klas(cid:200) Ship.
Specyfikacja
Oto specyfikacja zwrotu w lewo oparta na tych samych pomys(cid:239)ach, które wykorzystano wcze(cid:258)niej:
public void whenTurnLeftThenLeft() {
Location expected = location.copy();
expected.turnLeft();
ship.turnLeft();
assertEquals(ship.getLocation(), expected);
}
105
Poleć książkęKup książkęTDD. Programowanie w Javie sterowane testami
Kod rozwi(cid:200)zania
Napisanie kodu dla nowej specyfikacji prawdopodobnie nie sprawi(cid:239)o Ci trudno(cid:258)ci.
public void turnLeft() {
location.turnLeft();
}
Specyfikacja
Specyfikacja zwrotu w prawo powinna wygl(cid:200)da(cid:202) niemal identycznie jak specyfikacja zwrotu
w lewo.
public void whenTurnRightThenRight() {
Location expected = location.copy();
expected.turnRight();
ship.turnRight();
assertEquals(ship.getLocation(), expected);
}
Kod rozwi(cid:200)zania
Teraz zako(cid:241)cz prace nad omawianym wymaganiem i dodaj kod rozwi(cid:200)zania odpowiadaj(cid:200)cy
specyfikacji zwrotu w prawo.
public void turnRight() {
location.turnRight();
}
Kompletny kod (cid:283)ród(cid:239)owy dotycz(cid:200)cy tego wymagania znajdziesz w odga(cid:239)(cid:218)zieniu req3-left-right
w repozytorium tdd-java-ch04-ship. Kod ten jest dost(cid:218)pny na stronie https://bitbucket.org/
vfarcic/tdd-java-ch04-ship/branch/req03-left-right.
Wymaganie nr 4
Wszystkie dotychczasowe zadania by(cid:239)y stosunkowo (cid:239)atwe, poniewa(cid:285) potrzebne funkcje by(cid:239)y
dost(cid:218)pne w klasach pomocniczych. Dzi(cid:218)ki temu dowiedzia(cid:239)e(cid:258) si(cid:218), (cid:285)e trzeba si(cid:218) skupi(cid:202) na
rozwijanej jednostce, nie nale(cid:285)y natomiast próbowa(cid:202) testowa(cid:202) efektów ko(cid:241)cowych. W ten
sposób mo(cid:285)esz zbudowa(cid:202) zaufanie. Powiniene(cid:258) ufa(cid:202), (cid:285)e opracowany przez innych kod (klasy
pomocnicze) jest prawid(cid:239)owy. Pocz(cid:200)wszy od wymagania nr 4, b(cid:218)dziesz musia(cid:239) zacz(cid:200)(cid:202) wierzy(cid:202)
w poprawno(cid:258)(cid:202) kodu napisanego samodzielnie. Prace b(cid:218)d(cid:200) si(cid:218) toczy(cid:239)y w takim samym modelu
jak wcze(cid:258)niej. Napiszesz specyfikacj(cid:218), uruchomisz testy, wykryjesz niepowodzenie, napiszesz
kod rozwi(cid:200)zania, wykonasz testy, stwierdzisz, (cid:285)e ko(cid:241)cz(cid:200) si(cid:218) sukcesem, a w ostatnim kroku
przeprowadzisz refaktoryzacj(cid:218) (je(cid:258)li uznasz, (cid:285)e kod mo(cid:285)na poprawi(cid:202)). Staraj si(cid:218) my(cid:258)le(cid:202) o tym,
jak przetestowa(cid:202) jednostk(cid:218) (metod(cid:218)) bez zag(cid:239)(cid:218)biania si(cid:218) w dzia(cid:239)anie metod i klas wywo(cid:239)ywa-
nych przez t(cid:218) jednostk(cid:218).
106
Poleć książkęKup książkęRozdzia(cid:225) 4. • Testy jednostkowe
Po dodaniu obs(cid:239)ugi poszczególnych instrukcji (naprzód, w ty(cid:239), w lewo i w prawo) pora po(cid:239)(cid:200)czy(cid:202)
ca(cid:239)e rozwi(cid:200)zanie w jedn(cid:200) ca(cid:239)o(cid:258)(cid:202). Nale(cid:285)y utworzy(cid:202) metod(cid:218), która pozwoli przekaza(cid:202) dowoln(cid:200)
sekwencj(cid:218) polece(cid:241) w jednym (cid:239)a(cid:241)cuchu znaków. Ka(cid:285)de polecenie to litera: n oznacza „naprzód”,
w to „wstecz”, l to „lewo”, a p to „prawo”.
Statek przyjmuje (cid:239)a(cid:241)cuch znaków z poleceniami (literami nwlp, oznaczaj(cid:200)cymi naprzód, wstecz, lewo
i prawo).
Specyfikacja
Zacznij od polecenia z argumentem obejmuj(cid:200)cym tylko liter(cid:218) n (naprzód).
public void whenReceiveCommandsFThenForward() {
Location expected = location.copy();
expected.forward();
ship.receiveCommands( f );
assertEquals(ship.getLocation(), expected);
}
Ta specyfikacja wygl(cid:200)da niemal tak samo jak specyfikacja whenMoveForwardThenForward, jednak
tym razem wywo(cid:239)ywana jest metoda ship.receiveCommands( f ).
Kod rozwi(cid:200)zania
Wcze(cid:258)niej napisano ju(cid:285) o tym, jak wa(cid:285)ne jest pisanie mo(cid:285)liwie najprostszego kodu zgodnego
ze specyfikacj(cid:200).
Pisz jak najprostszy kod, który przejdzie testy. Dzi(cid:218)ki temu uzyskasz bardziej uporz(cid:200)dkowany
i przejrzysty projekt oraz unikniesz dodawania zb(cid:218)dnych funkcji
To podej(cid:258)cie oparte jest na za(cid:239)o(cid:285)eniu, (cid:285)e im prostszy jest kod rozwi(cid:200)zania, tym (cid:239)atwiejsza b(cid:218)dzie jego
konserwacja. Jest to zgodne z regu(cid:239)(cid:200) KISS. Mówi ona, (cid:285)e wi(cid:218)kszo(cid:258)(cid:202) systemów pracuje najlepiej, je(cid:258)li
systemy te s(cid:200) proste, a nie skomplikowane. Dlatego prostota powinna by(cid:202) podstawowym celem w trak-
cie tworzenia projektu. Nale(cid:285)y natomiast unika(cid:202) zb(cid:218)dnych komplikacji.
Jest to dobry moment na zastosowanie wspomnianej regu(cid:239)y. Mo(cid:285)liwe, (cid:285)e chcesz napisa(cid:202)
fragment kodu podobny do poni(cid:285)szego:
public void receiveCommands(String commands) {
if (commands.charAt(0) == f ) {
moveForward();
}
}
107
Poleć książkęKup książkęTDD. Programowanie w Javie sterowane testami
Ten przyk(cid:239)adowy kod sprawdza, czy pierwsza litera to f. Je(cid:258)li tak jest, kod wywo(cid:239)uje metod(cid:218)
moveForward. Istnieje te(cid:285) wiele innych mo(cid:285)liwych wersji tego kodu. Je(cid:285)eli jednak stosujesz si(cid:218)
do regu(cid:239)y zach(cid:218)caj(cid:200)cej do zachowania prostoty, powiniene(cid:258) napisa(cid:202) lepsze rozwi(cid:200)zanie:
public void receiveCommands(String command) {
moveForward();
}
Jest to najprostszy i najkrótszy kod, który pozwala przej(cid:258)(cid:202) test. Pó(cid:283)niej mo(cid:285)esz uzyska(cid:202) co(cid:258)
bardziej podobnego do pierwszej wersji kodu. Mo(cid:285)liwe, (cid:285)e zastosujesz p(cid:218)tl(cid:218) lub wymy(cid:258)lisz
inne rozwi(cid:200)zanie, gdy zadanie stanie si(cid:218) bardziej skomplikowane. Jednak na razie nale(cid:285)y si(cid:218)
skoncentrowa(cid:202) na jednej specyfikacji i na tworzeniu prostego kodu. Postaraj si(cid:218) zachowa(cid:202) ja-
sno(cid:258)(cid:202) umys(cid:239)u, skupiaj(cid:200)c si(cid:218) tylko na wykonywanym zadaniu.
W celu zachowania zwi(cid:218)z(cid:239)o(cid:258)ci pozosta(cid:239)e specyfikacje i rozwi(cid:200)zania (dla liter w, l i p) pomini(cid:218)to.
Potrzebny kod powiniene(cid:258) napisa(cid:202) samodzielnie. Nast(cid:218)pnie przejd(cid:283) do ostatniej specyfikacji
dla omawianego wymagania.
Specyfikacja
Teraz gdy kod potrafi ju(cid:285) przetwarza(cid:202) pojedyncze polecenia (niezale(cid:285)nie od tego, jaka jest
podana instrukcja), mo(cid:285)na doda(cid:202) mechanizm przesy(cid:239)ania (cid:239)a(cid:241)cuchów polece(cid:241). Oto specyfikacja
tego zadania:
public void whenReceiveCommandsThenAllAreExecuted() {
Location expected = location.copy();
expected.turnRight();
expected.forward();
expected.turnLeft();
expected.backward();
ship.receiveCommands( pnlw );
assertEquals(ship.getLocation(), expected);
}
Ta specyfikacja jest nieco d(cid:239)u(cid:285)sza od poprzednich, ale nie jest skomplikowana. Kod przeka-
zuje polecenia pnlw (czyli w prawo, naprzód, w lewo, wstecz) i oczekuje, (cid:285)e lokalizacja odpo-
wiednio si(cid:218) zmieni. Tak jak wcze(cid:258)niej, kod nie sprawdza wyniku ko(cid:241)cowego (czyli nie okre(cid:258)la,
czy zmieni(cid:239)y si(cid:218) wspó(cid:239)rz(cid:218)dne), a jedynie bada, czy u(cid:285)ywane s(cid:200) prawid(cid:239)owe wywo(cid:239)ania metod
pomocniczych.
Kod rozwi(cid:200)zania
Oto efekt ko(cid:241)cowy:
public void receiveCommands(String commands) {
for (char command : commands.toCharArray()) {
switch(command) {
case n :
moveForward();
108
Poleć książkęKup książkęRozdzia(cid:225) 4. • Testy jednostkowe
break;
case w :
moveBackward();
break;
case l :
turnLeft();
break;
case p :
turnRight();
break;
}
}
}
Je(cid:258)li próbowa(cid:239)e(cid:258) samodzielnie pisa(cid:202) specyfikacje i kod rozwi(cid:200)zania oraz stara(cid:239)e(cid:258) si(cid:218) zachowa(cid:202)
przy tym prostot(cid:218), w drodze do ostatecznego rozwi(cid:200)zania prawdopodobnie kilkakrotnie mu-
sia(cid:239)e(cid:258) przeprowadzi(cid:202) refaktoryzacj(cid:218). Prostota jest niezwykle istotna, a refaktoryzacja cz(cid:218)sto jest
korzystn(cid:200) konieczno(cid:258)ci(cid:200). W trakcie refaktoryzacji pami(cid:218)taj, (cid:285)e kod zawsze musi by(cid:202) zgodny
ze wszystkimi specyfikacjami.
Przeprowadzaj refaktoryzacj(cid:218) tylko wtedy, gdy wszystkie testy ko(cid:241)cz(cid:200) si(cid:218) powodzeniem
Korzy(cid:258)(cid:202) z tego jest taka, (cid:285)e refaktoryzacj(cid:218) mo(cid:285)na wtedy przeprowadzi(cid:202) bezpiecznie.
Je(cid:258)li ca(cid:239)y kod rozwi(cid:200)zania, który mo(cid:285)e zosta(cid:202) zmodyfikowany, jest pokryty testami, a wszystkie testy
ko(cid:241)cz(cid:200) si(cid:218) powodzeniem, refaktoryzacja jest stosunkowo bezpieczna. W wi(cid:218)kszo(cid:258)ci sytuacji refaktoryza-
cja nie wymaga dodawania nowych testów — wystarcz(cid:200) drobne zmiany w istniej(cid:200)cych testach. Gdy
przeprowadzasz refaktoryzacj(cid:218), wszystkie testy powinny si(cid:218) ko(cid:241)czy(cid:202) powodzeniem zarówno przed zmo-
dyfikowaniem kodu, jak i po jego zmianie.
Kompletny kod (cid:283)ród(cid:239)owy dotycz(cid:200)cy tego wymagania znajdziesz w odga(cid:239)(cid:218)zieniu req04-commands
w repozytorium tdd-java-ch04-ship. Kod ten jest dost(cid:218)pny na stronie https://bitbucket.org/
vfarcic/tdd-java-ch04-ship/branch/req04-commands.
Wymaganie nr 5
Ziemia, podobnie jak inne planety, jest kul(cid:200). Gdy Ziemi(cid:218) przedstawia si(cid:218) na mapie, po dotar-
ciu do jednego kra(cid:241)ca nale(cid:285)y przeskoczy(cid:202) do drugiego. Na przyk(cid:239)ad je(cid:258)li kierujesz si(cid:218) na
wschód i docierasz do najdalszego punktu na Pacyfiku, powiniene(cid:258) przeskoczy(cid:202) do zachod-
niej cz(cid:218)(cid:258)ci mapy i kontynuowa(cid:202) przemieszczanie si(cid:218) w kierunku Ameryki. Ponadto by upro-
(cid:258)ci(cid:202) poruszanie si(cid:218), mo(cid:285)na zdefiniowa(cid:202) map(cid:218) jako siatk(cid:218). Szeroko(cid:258)(cid:202) i wysoko(cid:258)(cid:202) na siatce nale(cid:285)y
wtedy przedstawi(cid:202) za pomoc(cid:200) osi X i Y. Tak wi(cid:218)c siatka b(cid:218)dzie mia(cid:239)a maksymaln(cid:200) szeroko(cid:258)(cid:202) (X)
i wysoko(cid:258)(cid:202) (Y).
Dodaj obs(cid:239)ug(cid:218) przeskakiwania od jednej kraw(cid:218)dzi siatki do innej.
109
Poleć książkęKup książkęTDD. Programowanie w Javie sterowane testami
Specyfikacja
Pierwsz(cid:200) rzecz(cid:200), jak(cid:200) trzeba zrobi(cid:202), jest przekazanie do konstruktora klasy Ship obiektu typu
Planet z maksymalnymi wspó(cid:239)rz(cid:218)dnymi osi X i Y. Na szcz(cid:218)(cid:258)cie Planet to jedna z ju(cid:285) gotowych
(i przetestowanych) klas pomocniczych. Teraz wystarczy utworzy(cid:202) obiekt tej klasy i przekaza(cid:202) go
do konstruktora klasy Ship.
public void whenInstantiatedThenPlanetIsStored() {
Point max = new Point(50, 50);
Planet planet = new Planet(max);
ship = new Ship(location, planet);
assertEquals(ship.getPlanet(), planet);
}
W tym kodzie zdefiniowano wymiary planety na 50 × 50 jednostek i przekazano je do obiektu
klasy Planet. Nast(cid:218)pnie ten obiekt przekazano do konstruktora klasy Ship. Mo(cid:285)e zauwa(cid:285)y(cid:239)e(cid:258),
(cid:285)e konstruktor b(cid:218)dzie wymaga(cid:239) teraz dodatkowego argumentu. W obecnej postaci kodu kon-
struktor przyjmuje tylko obiekt typu Location. Aby napisa(cid:202) kod dla nowej specyfikacji, trzeba
sprawi(cid:202), by konstruktor pobiera(cid:239) tak(cid:285)e obiekt typu Planet.
Jak napiszesz rozwi(cid:200)zanie dla nowej specyfikacji, nie naruszaj(cid:200)c przy tym (cid:285)adnych istniej(cid:200)cych
specyfikacji?
Kod rozwi(cid:200)zania
Zastosuj podej(cid:258)cie „od szczegó(cid:239)u do ogó(cid:239)u”. Zgodnie z asercj(cid:200) dost(cid:218)pny powinien by(cid:202) getter
dla obiektu planet.
private Planet planet;
public Planet getPlanet() {
return planet;
}
Konstruktor powinien przyjmowa(cid:202) obiekt typu Planet jako drugi argument i przypisywa(cid:202) go
do wcze(cid:258)niej dodanej zmiennej planet. Pierwszym pomys(cid:239)em mo(cid:285)e by(cid:202) dodanie nowego ar-
gumentu do istniej(cid:200)cego konstruktora, to jednak prowadzi do naruszenia wielu istniej(cid:200)cych
specyfikacji, u(cid:285)ywaj(cid:200)cych konstruktora jednoargumentowego. Pozostaje wi(cid:218)c tylko jedna mo(cid:285)-
liwo(cid:258)(cid:202) — napisa(cid:202) drugi konstruktor.
public Ship(Location location) {
this.location = location;
}
public Ship(Location location, Planet planet) {
this.location = location;
this.planet = planet;
}
Uruch
Pobierz darmowy fragment (pdf)