Test Driven Development with JUnit 5. Part 2

Test Driven Development with JUnit 5. Part 2

The second part in our article series on test driven development with JUnit 5. Happy reading.

2. The flight-management application


We are developing a flight-management application. Currently, the application is able to create and set up flights, and add passengers to and remove them from flights.

We’ll walk through scenarios that follow the developers’ everyday work. We’ll start with the non-TDD application, which is supposed to do several things such as follow company policies for regular and VIP passengers. We need to understand the application and make sure it is really implementing the expected operations. So, we have to cover the existing code with unit tests. Once we’ve done that, we’ll address another challenge: adding new functionality by first understanding what needs to be done, next writing tests that fail, and then writing the code that fixes the tests. This work cycle is one of the foundations of TDD.

The flight-management application is a Java application built with the help of Maven. The software must maintain a policy regarding adding passengers to and removing them from flights. Flights may be different types: currently, there are economy and business flights, but other types may be added later, depending on customer requirements. Both VIP passengers and regular customers may be added to economy flights, but only VIP passengers may be added to business flights.

Test Driven Development with JUnit 5.png


There is also a policy for removing passengers from flights: a regular passenger may be removed from a flight, but a VIP passenger cannot be removed. As we can see from these two activity diagrams, the initial business logic focuses on decision-making.

Test Driven Development with JUnit 5 2.png


Let's look at the initial design for this application. It has a field called flightType in the Flight class. Its value determines the behavior of the addPassenger and removePassenger methods. The developers need to focus on decision-making at the level of the code for these two methods.

Test Driven Development with JUnit 5 3.png


public class Passenger {

private String name; #1
private boolean vip; #2

public Passenger(String name, boolean vip) { #3
this.name = name; #3
this.vip = vip; #3
} #3

public String getName() { #4
return name; #4
} #4

public boolean isVip() { #5
return vip; #5
} #5

}

In this listing:

  • The Passenger class contains a name field #1 together with a getter for it #4
  • It also contains a VIP field #2 together with a getter for it #5
  • The constructor of the Passenger class initializes the name and VIP fields #3

The next listing shows the Flight class.

public class Flight {

private String id; #1
private List passengers = new ArrayList(); #2
private String flightType; #3

public Flight(String id, String flightType) { #4
this.id = id; #4
this.flightType = flightType; #4
} #4

public String getId() { #5
return id; #5
} #5

public List getPassengersList() { #6
return Collections.unmodifiableList(passengers); #6
} #6

public String getFlightType() { #7
return flightType; #7
} #7


public boolean addPassenger(Passenger passenger) {
switch (flightType) { #8
case "Economy": #9
return passengers.add(passenger); #9
case "Business": #10
if (passenger.isVip()) { #10
return passengers.add(passenger); #10
} #10
return false; #10
default: #11
throw new RuntimeException("Unknown type: " + flightType); #11
}

}

public boolean removePassenger(Passenger passenger) {
switch (flightType) { #12
case "Economy": #13
if (!passenger.isVip()) { #13
return passengers.remove(passenger); #13
} #13
return false; #13
case "Business": #14
return false; #14
default: #15
throw new RuntimeException("Unknown type: " + flightType); #15
}
}

}

  In this listing:

  • The Flight class contains an identifier #1 together with a getter for it #5, a list of passengers initialized as an empty list #2 together with a getter for it #6, and a flight type #3 together with a getter for it #7.
  • The constructor of the Flight class initializes the id and the flightType fields #4.
  • The addPassenger method checks the flight type #8. If it is an economy flight, any passengers can be added #9. If it is a business flight, only VIP passengers can be added #10. Otherwise (if the flight is neither an economy nor a business flight), the method will throw an exception, as it cannot handle an unknown flight type #11.
  • The removePassenger method checks the flight type #12. If it is an economy flight, only regular passengers can be removed #13. If it is a business flight, passengers cannot be removed #14. Otherwise (if the flight is neither an economy nor a business flight), the method will throw an exception, as it cannot handle an unknown flight type #15.

The application has no tests yet. Instead, the initial developers wrote some code in which they simply followed the execution and compared it with their expectations. For example, there is an Airport class, including a main method that acts as a client of the Flight and Passenger classes and works with the different types of flights and passengers.

public class Airport {

public static void main(String[] args) {
Flight economyFlight = new Flight("1", "Economy"); #1
Flight businessFlight = new Flight("2", "Business"); #1

Passenger james = new Passenger("James", true); #2
Passenger mike = new Passenger("Mike", false); #2

businessFlight.addPassenger(james); #3
businessFlight.removePassenger(james); #3
businessFlight.addPassenger(mike); #4
economyFlight.addPassenger(mike); #5

System.out.println("Business flight passengers list:"); #6
for (Passenger passenger: businessFlight.getPassengersList()) { #6
System.out.println(passenger.getName()); #6
} #6

System.out.println("Economy flight passengers list:"); #7
for (Passenger passenger: economyFlight.getPassengersList()) { #7
System.out.println(passenger.getName()); #7
} #7
}
}

In this listing:

  • We initialize an economy flight and a business flight #1. We also initialize James as a VIP passenger and Mike as a regular passenger #2.
  • We try to add James to and remove him from the business flight #3, and then we try to add Mike to and remove him from the business flight #4 and the economy flight #5.
  • We print the list of passengers on the business flight #6 and the economy flight #7.

The result of running this program is shown in figure 20.4. James, a VIP passenger, has been added to the business flight, and we could not remove him. Mike, a regular passenger, could not be added to the business flight, but we were able to add him to the economy flight.

Test Driven Development with JUnit 5 4.png


So far, things are working as expected, following the policies that we previously defined. To build a reliable application and to be able to easily and safely understand and implement the business logic, we move the application to the TDD approach.

Interested in JUnit? Check out our trainings.


Catalin Tudose
Java and Web Technologies Expert
Still have questions?
Connect with us