Course Scenario
Estimated time to read: 5 minutes
Overview¶
This course uses a Java application implementing the business logic for the management of flights and passengers, following a set of policies.
- The input for this scenario is a non-TDD working application.
- The application has a suite of expected operations, however, this must be confirmed.
- Unit tests will be developed around the existing code in the application.
- Functionality is added by understanding what needs to be done, writing tests that initially fail, then writing code to fix the test.
Assume that you are joining the development of a program to manage flights and passengers. The company maintains supports regarding adding and removing a passenger to a flight.
Initial Application Design¶
classDiagram
Flight --|> "has" Passenger
class Flight {
-String flightType
+boolean addPassenger(Passenger passenger)
+boolean removePassenger(Passenger passenger)
}
class Passenger{
}
Business Logic¶
Passenger Addition¶
The flight may be of a few types, economy and business are known at the time, consider that there may be a need to add other types at a later time, depending on the requirements from the customer.
If the flight is an economy flight, both standard and VIP passengers may be added.
If the flight is a business flight. only VIP passengers may be added.
%%{init: {'flowchart' : {'curve' : 'linear'}}}%%
graph
AA(( ))
ZZ(( ))
A[Add passenger to the flight]
B{ }
C{ }
D[Approve request]
E[Approve request]
F[Reject request]
G{ }
H{ }
AA --> A
A --> | Is Flight Economy?| B
B --> |Yes| D
D --> H
B --> | Is Passenger VIP?| C
C --> | Yes| E
C --> F
E --> G
F --> G
G --> H
H --> ZZ
Passenger Removal¶
There is also a policy for removing a passenger from the flight. A standard passenger may be removed from a flight, a VIP passenger cannot be removed.
%%{init: {'flowchart' : {'curve' : 'linear'}}}%%
graph
AA(( )) --> A
ZZ(( ))
A[Remove passenger from flight] --> | Is passenger VIP?| B{ }
B -->|Yes| C[Reject Request]
B -->|No| D[Approve Request]
E{ } --> ZZ
C --> E
D --> E
Conditional to Polymorphism¶
Conditional¶
classDiagram
class Flight {
-String flightType
+boolean addPassenger(Passenger passenger)
+boolean removePassenger(Passenger passenger)
}
To test this, a switch case would have been implemented, however, as no 'default' case is given, this section of code would never be tested. Thus a polymorphic design would prove beneficial.
public boolean removePassenger(Passenger passenger) {
switch(flightType) {
case "Economy":
{}
case "Business":
{}
default:
{}
}
}
Polymorphic¶
A separate class should be introduced for each conditional type. In the example above, this shows each type of FlightType being converted into its own class.
classDiagram
Flight <|-- EconomyFlight
Flight <|-- BusinessFlight
Flight <|-- PremiumFlight
class Flight{
+boolean addPassenger(Passenger passenger)
+boolean removePassenger(Passenger passenger)
}
class EconomyFlight{
}
class BusinessFlight{
}
class PremiumFlight{
}
Initial Codebase¶
package io.entityfour.tdd.airport;
public class Airport {
public static void main(String[] args) { // Contains a main method that serves, mainly, for testing purposes
Flight economyFlight = new Flight("1", "Economy"); // Flight 1 is Economy
Flight businessFlight = new Flight("2", "Business"); /// Flight 2 is Business
Passenger john = new Passenger("John", true); // VIP named John
Passenger mike = new Passenger("Mike", false); // Standard named Mike
businessFlight.addPassenger(john); // John is added to the flight
businessFlight.removePassenger(john); // Then an attempt is made to remove him, this should fail due to company policy
businessFlight.addPassenger(mike); // Mike is attempted to be added to the business flight, this should fail due to company policy
economyFlight.addPassenger(mike); // Mike is added to the economy flight
System.out.println("Business flight passengers list:");
for (Passenger passenger: businessFlight.getPassengersList()) {
System.out.println(passenger.getName());
}
System.out.println("Economy flight passengers list:");
for (Passenger passenger: economyFlight.getPassengersList()) {
System.out.println(passenger.getName());
}
}
}
package io.entityfour.tdd.airport;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Flight {
private String id; // Flight ID
private List<Passenger> passengersList = new ArrayList<Passenger>(); // List of passengers
private String flightType; // Flight Type
public Flight(String id, String flightType) {
this.id = id;
this.flightType = flightType;
}
public String getId() {
return id;
}
public List<Passenger> getPassengersList() {
return Collections.unmodifiableList(passengersList);
}
public String getFlightType() {
return flightType;
}
public boolean addPassenger(Passenger passenger) { // Contains logic for adding a passenger
switch (flightType) {
case "Economy":
return passengersList.add(passenger);
case "Business":
if (passenger.isVip()) {
return passengersList.add(passenger);
}
return false;
default:
throw new RuntimeException("Unknown type: " + flightType);
}
}
public boolean removePassenger(Passenger passenger) { // Contains logic for removing a passenger
switch (flightType) {
case "Economy":
if (passenger.isVip()) {
return passengersList.remove(passenger);
}
return false;
case "Business":
return false;
default:
throw new RuntimeException("Unknown type: " + flightType);
}
}
}
package io.entityfour.tdd.airport;
public class Passenger {
private String name; // Passengers Name
private boolean vip; // VIP Flag / Status
public Passenger(String name, boolean vip) {
this.name = name;
this.vip = vip;
}
public String getName() {
return name;
}
public boolean isVip() {
return vip;
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.entityfour.tdd</groupId>
<artifactId>TDD</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>TDD</name>
<url>http://maven.apache.org</url>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>1.0.1</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.9</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>jacoco-check</id>
<phase>test</phase>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>PACKAGE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.0.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>