Table of contents:

  1. Adding passanger busimess logic
  2. Removing passanger business logic
  3. From TDD to the BDD

Adding passanger busimess logic

To undersant the story lets use some simple example: the comapny maintains a policy regarding adding and removing a passanger to a flight. The flights may be a few types:

  • economy
  • business
  • other added at the later time

If the flight is an economy one, most VIP passangers and usual ones may be added to it. If the flight ia s business one, only VIP passangers may be added to it.

Adding Passanger Business Logic
Adding Passanger Business Logic

Removing passanger business logic

And there is also a policy for removing a passanger from the flight. It also involves answering to yes/no questions. A usual passanger may be removed from a flight, a VIP passenger cannot be removed.

Removing Passenger Business Logic
Removing Passenger Business Logic

Usual passenger can be remove fron a flight, a VIP passenger cannot be removed. As we can see from these two activity diagrams, the inital business logic focus on decision making.

Below is a the simple class of the application. The flight class has two sub-classes: EconomyFlight and: BusinessFlight. The police of adding a passannger and removing a passenger are encapsulated into: addPassenger and: removePassanger methods. As these methos are inheruted from the Flight class and are at the level of each sublcass, we may say that the decision of each policy to apply will be preactically made at runtime through polymorphism.

Initial Application Design
Initial Application Design

From TDD to the BDD

Below are test that have been build as TDD. We have here 4 tests. From theit names, we understand that we have two flights, economy and business and two passangers: usual and VIP. We are doing some operations with this pairs.

 @Test
    public void testEconomyFlightUsualPassenger() {
        Flight economyFlight = new EconomyFlight("1");
        Passenger andrew = new Passenger("Andrew", false);
        Assertions.assertEquals("1", economyFlight.getId());
        Assertions.assertEquals(true, economyFlight.addPassenger(andrew));
        Assertions.assertEquals(1, economyFlight.getPassengersList().size());
        Assertions.assertEquals("Andrew", ((Passenger)economyFlight.getPassengersList().get(0)).getName());
        Assertions.assertEquals(true, economyFlight.removePassenger(andrew));
        Assertions.assertEquals(0, economyFlight.getPassengersList().size());
    }

    @Test
    public void testEconomyFlightVipPassenger() {
        Flight economyFlight = new EconomyFlight("1");
        Passenger john = new Passenger("John", true);
        Assertions.assertEquals("1", economyFlight.getId());
        Assertions.assertEquals(true, economyFlight.addPassenger(john));
        Assertions.assertEquals(1, economyFlight.getPassengersList().size());
        Assertions.assertEquals("John", ((Passenger)economyFlight.getPassengersList().get(0)).getName());
        Assertions.assertEquals(false, economyFlight.removePassenger(john));
        Assertions.assertEquals(1, economyFlight.getPassengersList().size());
    }

    @Test
    public void testBusinessFlightUsualPassenger() {
        Flight businessFlight = new BusinessFlight("2");
        Passenger andrew = new Passenger("Andrew", false);
        Assertions.assertEquals(false, businessFlight.addPassenger(andrew));
        Assertions.assertEquals(0, businessFlight.getPassengersList().size());
        Assertions.assertEquals(false, businessFlight.removePassenger(andrew));
        Assertions.assertEquals(0, businessFlight.getPassengersList().size());
    }

    @Test
    public void testBusinessFlightVipPassenger() {
        Flight businessFlight = new BusinessFlight("2");
        Passenger john = new Passenger("John", true);
        Assertions.assertEquals(true, businessFlight.addPassenger(john));
        Assertions.assertEquals(1, businessFlight.getPassengersList().size());
        Assertions.assertEquals(false, businessFlight.removePassenger(john));
        Assertions.assertEquals(1, businessFlight.getPassengersList().size());
    }

This tests are good but:

  • they do not discuss which the expectations are, which makes them harder to understand and to fix if they break
  • they do not seem much about what they are actually tesing

To change to better we can adopt some conventions:

  • use JUnit5 features more effectively
  • use nestet tests
  • reduce code application by reinitialize object before each test
  • attach to test lables to replace the long names to express in plain English what we are doing

Below is simple test written in new way:

@Test
@DisplayName("Then you can add and remove him from an economy flight")
public void testAddAndRemove() {
  Assertions.assertAll("Verify all conditions for a usual passenger and an economy flight", new Executable[]{
      () -> {Assertions.assertEquals("1", EconomyFlightTest.this.economyFlight.getId());}, 
      () -> {Assertions.assertEquals(true, EconomyFlightTest.this.economyFlight.addPassenger(EconomyFlightTest.this.andrew));}, 
      () -> {Assertions.assertEquals(1, EconomyFlightTest.this.economyFlight.getPassengersList().size());}, 
      () -> {Assertions.assertEquals("Andrew", ((Passenger)EconomyFlightTest.this.economyFlight.getPassengersList().get(0)).getName());}, 
      () -> {Assertions.assertEquals(true, EconomyFlightTest.this.economyFlight.removePassenger(EconomyFlightTest.this.andrew));}, 
      () -> {Assertions.assertEquals(0, EconomyFlightTest.this.economyFlight.getPassengersList().size());
  }});
}

This is a first step from TDD to the BDD application, introducing methods with significant names using nested tests and annotating them. This way helps us to claryfy tests but we use only JUnit5 and it’s annotations. This is not full BDD and to use full features ofd BDD we will switch to the special frameworks.