Table of contents:

  1. JBehave Functionality
  2. Working with code
  3. JBehave Installation and configuration
  4. First story file

JBehave Functionality

There are few alternatives to choosing a BDD framework. and we’ll also take a look at the very popular one, JBahave. As implementing the idea of behaviour-driven development, JBehave allows us to write stories in plain text to be understood by all persons involved in the project. Through the stories, we’ll define scenarios that express the desired behaviour. Then, steps need to be mapped to a Java POJO. JBehave maps textual steps to Java methods via candidate steps. The scenario writer needs to provide annotated methods that match by regex patterns the textural steps.

Configure stories. JBehave runs the stories with an embedder, an entry point to all JBehave functionalities that are embeddable into launchers such as IDEs or CLIs. JBehave complements the embedder with an embeddable, which represents a runnable facade to the embedder. Finally, the JBehave core module contains support for running stories as JUnit tests. As other BDD frameworks, JBehave provides it’s terminology. We have to mention here, a story. This represents an automatically executable increment of business functionality, a slice through a feature which is functional, and on which we can get feedback. It covers one or more scenarios.

A scenario - this represents a real-life situations of the behavior of the application. A steps is a define using the classic BDD keywords: Given, When and Than.

Working with code

We use some application that simulates some BDD approach bu using JUnit 5 facilities and annotations, and the tests have been rewritten with this purpose in mind. Below is the code:

public class BusinessFlight extends Flight {
	
	public BusinessFlight(String id) {
		super(id);
	}

	@Override
	public boolean addPassenger(Passenger passenger) {
		if (passenger.isVip()) {
			return passengersList.add(passenger);
		}
		return false;
	}

	@Override
	public boolean removePassenger(Passenger passenger) {
		return false;
	}

}
public class EconomyFlight extends Flight {

	public EconomyFlight(String id) {
		super(id);
	}

	@Override
	public boolean addPassenger(Passenger passenger) {
		return passengersList.add(passenger);
	}

	@Override
	public boolean removePassenger(Passenger passenger) {
		if (!passenger.isVip()) {
			return passengersList.remove(passenger);
		}
		return false;
	}

}
public abstract class Flight {

	private String id;
	List<Passenger> passengersList = new ArrayList<Passenger>();

	public Flight(String id) {
		this.id = id;
	}

	public String getId() {
		return id;
	}

	public List<Passenger> getPassengersList() {
		return Collections.unmodifiableList(passengersList);
	}

	public abstract boolean addPassenger(Passenger passenger);

	public abstract boolean removePassenger(Passenger passenger);

}
public class Passenger {

	private String name;
	private boolean vip;

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

	public String getName() {
		return name;
	}

	public boolean isVip() {
		return vip;
	}

}

JBehave Installation and configuration

First in order to working with JBehave, we’ll install the plugin for IntelliJ. We’ll choose JBehave Support, and JBehave Step Generator.

Plugins: JBehave Support & JBehave Generator
Plugins: JBehave Support & JBehave Generator

First story file

To add story, we should add story file into test resources folder and called it: <name_of_file>.story

Meta: Passengers Policy
      The company follows a policy of adding and removing passengers, depending on the passenger type
      and on the flight type

Narrative:
As a company
I want to be able to manage passengers and flights
So that the policies of the company are followed

Scenario: Economy flight, usual passenger
Given there is an economy flight
When we have a usual passenger
Then you can add and remove him from an economy flight

Scenario: Economy flight, VIP passenger
Given there is an economy flight
When we have a VIP passenger
Then you can add him but cannot remove him from an economy flight

Scenario: Business flight, usual passenger
Given there is an business flight
When we have a usual passenger
Then you cannot add or remove him from a business flight

Scenario: Business flight, VIP passenger
Given there is an business flight
When we have a VIP passenger
Then you can add him but cannot remove him from a business flight

Base on this file we can generate java file that contains all the steps. We write the scenario and translate it into code. Given there is an economy flight, when we have a usual passanger, then you can add or remove him from an economy flight.

public class PassengersPolicy {
    private Flight economyFlight;
    private Flight businessFlight;
    private Passenger andrew;
    private Passenger john;

    @Given("there is an economy flight")
    public void givenThereIsAnEconomyFlight() {
        economyFlight = new EconomyFlight("1");
    }

    @When("we have a usual passenger")
    public void whenWeHaveAUsualPassenger() {
        andrew  = new Passenger("Andrew", false);
    }

    @Then("you can add and remove him from an economy flight")
    public void thenYouCanAddAndRemoveHimFromAnEconomyFlight() {
        assertAll("Verify all conditions for a usual passenger and an economy flight",
                () -> assertEquals("1", economyFlight.getId()),
                () -> assertEquals(true, economyFlight.addPassenger(andrew)),
                () -> assertEquals(1, economyFlight.getPassengersList().size()),
                () -> assertEquals("Andrew", economyFlight.getPassengersList().get(0).getName()),
                () -> assertEquals(true, economyFlight.removePassenger(andrew)),
                () -> assertEquals(0, economyFlight.getPassengersList().size())
        );
    }
}

In order to be able to run these tests, we need a new special class that will represent the test configuration. This class will extend JUnitStory and for the moment it will tell us the following, that is configuration is the most useful configuration, and to display the report on the console. The steps definition is to be found in the PassengerPlicy class.

public class PassengersPolicyStory extends JUnitStory {

    @Override
    public Configuration configuration() {
        return new MostUsefulConfiguration()
                .useStoryReporterBuilder(new StoryReporterBuilder().withDefaultFormats()
                                                                   .withFormats(Format.CONSOLE, Format.TXT, Format.HTML, Format.XML));
    }

    @Override
    public InjectableStepsFactory stepsFactory() {
        return new InstanceStepsFactory(configuration(), new PassengersPolicy());
    }
}

This line:

.withFormats(Format.CONSOLE, Format.TXT, Format.HTML, Format.XML));

allows to display output in more formats:

  • text formaty
  • html format
  • xml format
The next part is to cover VIP passenger: 

    @When("we have a VIP passenger")
    public void whenWeHaveAVipPassenger() {
        john = new Passenger("John", true);
    }

    @Then("you can add him but cannot remove him from an economy flight")
    public void thenYouCanAddHimButCannotRemoveHimFromAnEconomyFlight() {
        assertAll("Verify all conditions for a VIP passenger and an economy flight",
                () -> assertEquals("1", economyFlight.getId()),
                () -> assertEquals(true, economyFlight.addPassenger(john)),
                () -> assertEquals(1, economyFlight.getPassengersList().size()),
                () -> assertEquals("John", economyFlight.getPassengersList().get(0).getName()),
                () -> assertEquals(false, economyFlight.removePassenger(john)),
                () -> assertEquals(1, economyFlight.getPassengersList().size())
        );
    }

The last part is to cover business flight:

  @Given("there is an business flight")
    public void givenThereIsAnBusinessFlight() {
        businessFlight = new BusinessFlight("2");
    }

    @Then("you cannot add or remove him from a business flight")
    public void thenYouCannotAddOrRemoveHimFromABusinessFlight() {
        assertAll("Verify all conditions for a usual passenger and a business flight",
                () -> assertEquals(false, businessFlight.addPassenger(andrew)),
                () -> assertEquals(0, businessFlight.getPassengersList().size()),
                () -> assertEquals(false, businessFlight.removePassenger(andrew)),
                () -> assertEquals(0, businessFlight.getPassengersList().size())
        );
    }

    @Then("you can add him but cannot remove him from a business flight")
    public void thenYouCanAddHimButCannotRemoveHimFromABusinessFlight() {
        assertAll("Verify all conditions for a VIP passenger and a business flight",
                () -> assertEquals(true, businessFlight.addPassenger(john)),
                () -> assertEquals(1, businessFlight.getPassengersList().size()),
                () -> assertEquals(false, businessFlight.removePassenger(john)),
                () -> assertEquals(1, businessFlight.getPassengersList().size())
        );
    }

Reference:

  1. Behavior Driven Development Vol. 1
  2. Behavior Driven Development Vol. 2