Behavior Driven Development Vol. 2
Table of contents:
Adding passanger busimess logic
To understand the story let’s use some simple examples: the company maintains a policy regarding adding and removing a passenger to a flight. The flights may be a few types:
- economy
- business
- other added at the later time
If the flight is an economic one, most VIP passengers and usual ones may be added to it. If the flight is s a business one, only VIP passengers may be added to it.
Removing passanger business logic
And there is also a policy for removing a passenger from the flight. It also involves answering yes/no questions. A usual passenger may be removed from a flight, a VIP passenger cannot be removed.
Usual passenger can be removed from a flight, a VIP passenger cannot be removed. As we can see from these two activity diagrams, the initial business logic focus on decision making.
Below is a simple class of the application. The flight class has two sub-classes: EconomyFlight
and: BusinessFlight
. The police of adding a passenger and removing a passenger are encapsulated into: addPassenger
and: removePassanger
methods. As these methods are inherited from the Flight
class and are at the level of each subclass, we may say that the decision of each policy to apply will be practically made at runtime through polymorphism.
From TDD to the BDD
Below are tested that has been build as TDD. We have here 4 tests. From their names, we understand that we have two flights, economy and business and two passengers: usual and VIP. We are doing some operations with these 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());
}
These tests are good but:
- they do not discuss what 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 testing
To change to better we can adopt some conventions:
- use JUnit5 features more effectively
- use nested tests
- reduce code application by reinitializing objects before each test
- attach to test labels 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 the first step from TDD to the BDD application, introducing methods with significant names using nested tests and annotating them. This way helps us to clarify tests but we use only JUnit5 and its annotations. This is not full BDD and to use full features of BDD we will switch to the special frameworks.
Reference:
- Behavior Driven Development Vol. 1
- Behavior Driven Development Vol. 3
- https://thatoneprivacysite.net
My site is free of ads and trackers. Was this post helpful to you? Why not
Disqus is great for comments/feedback but I had no idea it came with these gaudy ads.