Skip to content

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

Airport.java
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());
  }
 }
}
Flight.java
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);
   }
 }

}
Passenger.java
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;
 }

}
pom.xml
<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>