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.

pa? 4, 2021 246
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.

Test Driven Development with JUnit 5. Part 6.jpg


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



Udostępnij


Masz jeszcze jakieś pytania?
Skontaktuj się z nami
Thank you.
Your request has been received.