Test Driven Development z użyciem JUnit 5. Część 6
Ostatni artykuł z naszej serii poświęconej programowaniu sterowanemu testami za pomocą JUnit 5. Miłej lektury.
Przechodzimy teraz do implementacji klasy PremiumFlight i jej logiki. Stworzymy PremiumFlight jako podklasę Flight i nadpisujemy metody addPassenger i removePassenger, ale zachowują się one jak stubs — nic nie robią i po prostu zwracają fałsz. Ich zachowanie zostanie później przedłużone. Praca w stylu TDD obejmuje najpierw tworzenie testów, a następnie logikę biznesową.
public class PremiumFlight extends Flight { #1
public PremiumFlight(String id) { #2
super(id); #2
} #2
@Override
public boolean addPassenger(Passenger passenger) { #3
return false; #3
} #3
@Override
public boolean removePassenger(Passenger passenger) { #4
return false; #4
} #4
W tym listingu:
- Deklarujemy klasę PremiumFlight, która rozszerza Flight # 1 i tworzymy dla niej konstruktora # 2.
- Tworzymy metody addPassenger # 3 i usuwamy metody # 4 jako stubs, bez żadnej logiki biznesowej. Po prostu zwracają fałsz.
Teraz wdrażamy testy zgodnie z logiką biznesową lotów premium z “figueres” 20.8 i 20.9.
public class AirportTest {
[…]
@DisplayName(“Given there is a premium flight”) #1
@Nested #1
class PremiumFlightTest { #1
private Flight premiumFlight; #2
private Passenger mike; #2
private Passenger james; #2
@BeforeEach
void setUp() {
premiumFlight = new PremiumFlight(“3”); #3
mike = new Passenger(“Mike”, false); #3
james = new Passenger(“James”, true); #3
}
@Nested #4
@DisplayName(“When we have a regular passenger”) #4
class RegularPassenger { #4
@Test #5
@DisplayName(“Then you cannot add or remove him #5
from a premium flight”) #5
public void testPremiumFlightRegularPassenger() { #5
assertAll(“Verify all conditions for a regular passenger #6
and a premium flight”, #6
() -> assertEquals(false, #7
premiumFlight.addPassenger(mike)), #7
() -> assertEquals(0, #7
premiumFlight.getPassengersList().size()), #7
() -> assertEquals(false, #8
premiumFlight.removePassenger(mike)), #8
() -> assertEquals(0, #8
premiumFlight.getPassengersList().size()) #8
);
}
}
@Nested #9
@DisplayName(“When we have a VIP passenger”) #9
class VipPassenger { #9
@Test #10
@DisplayName(“Then you can add and remove him #10
from a premium flight”) #10
public void testPremiumFlightVipPassenger() { #10
assertAll(“Verify all conditions for a VIP passenger #11
and a premium flight”, #11
() -> assertEquals(true, #12
premiumFlight.addPassenger(james)), #12
() -> assertEquals(1, #12
premiumFlight.getPassengersList().size()), #12
() -> assertEquals(true, #13
premiumFlight.removePassenger(james)), #13
() -> assertEquals(0, #13
premiumFlight.getPassengersList().size()) #13
);
}
}
}
}
W tym listingu:
- Deklarujemy zagnieżdżoną klasę PremiumFlightTest # 1, która zawiera pola reprezentujące lot i pasażerów # 2, które są ustawiane przed każdym testem # 3.
- Tworzymy dwie klasy zagnieżdżone na drugim poziomie w PremiumFlightTest: RegularPassenger # 4 i VipPassenger # 9. Używamy adnotacji JUnit 5 @DisplayName, aby oznaczyć te klasy, zaczynając od słowa kluczowego When.
- Wstawiamy jeden test do każdej z nowo dodanych klas RegularPassenger # 5 i VipPassenger # 10.
- Oznaczamy te testy adnotacją JUnit 5 @DisplayName zaczynającą się od słowa kluczowego Then.
- Testując lot premium i zwykłego pasażera, używamy metody assertAll do weryfikacji wielu warunków # 6. Sprawdzamy, czy nie może dodać pasażera do lotu premium i czy próba dodania pasażera nie zmienia rozmiaru listy pasażerów # 7. Następnie sprawdzamy, czy nie możemy usunąć pasażera z lotu premium i czy próba usunięcia pasażera nie zmienia rozmiaru listy pasażerów nr 8.
- Testując lot premium i pasażera VIP, ponownie używamy assertAll # 11. Sprawdzamy, czy możemy dodać pasażera do lotu premium i czy to zwiększa rozmiar listy pasażerów # 12. Następnie sprawdzamy, czy możemy usunąć pasażera z lotu premium i czy spowoduje to zmniejszenie rozmiaru listy pasażerów # 13.
Jeden z testów teraz kończy się niepowodzeniem, ale to nie jest problem. Wręcz przeciwnie: tego się spodziewaliśmy. Pamiętaj, że praca w stylu TDD oznacza bycie sterowanym przez testy, więc najpierw tworzymy test, który kończy się niepowodzeniem, a następnie piszemy fragment kodu, który sprawi, że test przejdzie pomyślnie. Ale jest jeszcze jedna niezwykła rzecz: test na lot premium i zwykłego pasażera jest już zielony. Oznacza to, że istniejąca logika biznesowa (metody addPassenger i removePassenger zwracają wartość false) jest wystarczająca w tym przypadku. Rozumiemy, że musimy skupić się tylko na pasażerze VIP. Cytując ponownie Kenta Becka: “TDD pomaga Ci zwracać uwagę na właściwe kwestie we właściwym czasie, dzięki czemu możesz uczynić swoje projekty bardziej przejrzystymi, możesz je udoskonalać w miarę uczenia się. TDD pozwala z czasem zyskać zaufanie do kodu”.
Wracamy więc do klasy PremiumFlight i dodajemy logikę biznesową tylko dla pasażerów VIP. Kierując się testami, przechodzimy od razu do rzeczy.
public class PremiumFlight extends Flight {
public PremiumFlight(String id) {
super(id);
}
@Override
public boolean addPassenger(Passenger passenger) {
if (passenger.isVip()) { #1
return passengers.add(passenger); #1
} #1
return false;
}
@Override
public boolean removePassenger(Passenger passenger) {
if (passenger.isVip()) { #2
return passengers.remove(passenger); #2
} #2
return false;
}
}
W tym listingu:
- Dodajemy pasażera tylko wtedy, gdy pasażer jest VIPem nr 1.
- Usuwamy pasażera tylko wtedy, gdy jest to VIP # 2.
- Testy działają dobrze, a pokrycie kodu wynosi 100%.
Wnioski
W tym artykule omówiono następujące kwestie:
- Zbadaliśmy koncepcje TDD i pokazaliśmy, w jaki sposób pomaga nam tworzyć bezpieczne aplikacje, ponieważ testy zapobiegają wprowadzaniu błędów do działającego kodu i stanowią część dokumentacji.
- Przygotowaliśmy aplikację inną niż TDD do przeniesienia do TDD poprzez dodanie hierarchicznych testów JUnit 5, które obejmują istniejącą logikę biznesową.
- Zrobiliśmy refaktoryzację i poprawę jakości kodu tej aplikacji TDD poprzez zastąpienie warunkowego polimorfizmem w oparciu o opracowane przez nas testy.
- Wdrażaliśmy nowe funkcje w stylu TDD, zaczynając od pisania testów, a następnie implementując logikę biznesową.
Interesujesz się JUnit? Sprawdź nasze szkolenia
Catalin Tudose
Java and Web Technologies Expert
Originally published at https://www.luxoft-training.pl.