The final step of the BDD process is to take scenarios that have been captured from the collaborative conversation and automate them to help guide development. Let's take a look at an example:
Automating an acceptance test
For this guide we are going to go through the Outside in Development flow and create our first automated acceptance test based of the following captured Gherkin scenario:
Scenario: Create a booking in Hotel Booking
Given a user wants to make a booking
When the booking is submitted by the user
Then the booking is successfully stored
Getting setup
Before we start automating our acceptance test we have a couple of things to get setup:
An automation framework to run our feature files
Access to our features files created by Behave Pro
Setting up a framework
For this automated acceptance test we are going to create a framework that uses two main libraries for framework Cucumber-JVM and Selenium-WebDriver. We'll do this via the cucumber-archetype
plugin by running the following in your terminal or command window:
mvn archetype:generate \
-DarchetypeGroupId=io.cucumber \
-DarchetypeArtifactId=cucumber-archetype \
-DarchetypeVersion=2.3.1.2 \
-DgroupId=behaveprodemo \
-DartifactId=behaveprodemo \
-Dpackage=behaveprodemo \
-Dversion=1.0.0-SNAPSHOT \
-DinteractiveMode=false
Once built, you should have a maven project named behaveprodemo
that you can navigate into.
Accessing our feature files
Our next step is to populate our framework with feature files we have created in Behave Pro. We can do this by integrating Behave Pro with a GitHub repository that contains your framework code. Meaning every time a feature file is created or updated, your framework is updated, giving us greater control of maintaining our automated acceptance tests.
To start with, create a new GitHub repository and then clone it to your local machine:
git clone git@github.com:<url-to-repo>.git
Next copy the contents of behaveprodemo
into your cloned folder and commit them to GitHub
git add .
git commit -m "Adding in Behave Pro demo"
git push origin master
With our framework committed to our repository the final step is to configure our JIRA project to connect to our repository. Simply do this by heading to Your project > Project Settings > BDD Integration
and following the wizard, ensuring you select:
Your organisation
Your repository
A branch you want to work on, such as
master
And set the feature directory to
/src/test/resource/behaveprodemo
which is where Cucumber-JVM will expect the feature files to live
Once everything has been setup go back to your locally cloned repository and run git pull origin
to pull down the feature files that Behave Pro is now storing in your GitHub repository.
Creating your first automated acceptance test
Now that we are setup we can start creating our automated acceptance test that will attempt to interact with our feature and fail because the feature doesn't exist.
Generate step definitions
First of all we need to create step definitions that will trigger our automation code. Step definitions match to the natural language steps that are defined in a Gherkin scenario. So as a scenario is run it will find the matching step definition to the step it's running and execute the code within it.
To create the code navigate into the root folder of your repository and run mvn test
to get output similar to this:
Notice how the annotated Given, When and Thens at the top of the declared methods match the Gherkin scenario. This is what we mean by matching step definitions to the natural language steps.
Since Cucumber-JVM has helpfully created us the code we need, we can copy this code directly into src/test/java/behaveprodemo/Stepdefs.java
and then run mvn test
again:
Notice how the output has now changed. This is because the step definitions we pasted in are now being triggered during the run and the first step is returning a pending exception.
Filling in the step definitions
Now that our step definitions are configured, our next step is to add in our failing automation code, which we will do using Selenium-WebDriver. To use Selenium we will need to add a new dependency to our pom.xml:
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.14.0</version>
<scope>test</scope>
</dependency>
We will also need to download the Chromedriver binary for Selenium to interact with which can be found here: https://chromedriver.storage.googleapis.com/index.html?path=2.41/
With everything setup let's update our step definitions to the following:
private WebDriver driver;
@Given("^a user wants to make a booking$")
public void a_user_wants_to_make_a_booking() throws Exception {
System.setProperty("webdriver.chrome.driver", "chromedriver");
driver = new ChromeDriver();
driver.navigate().to("file://index.html");
}
@When("^the booking is submitted by the user$")
public void the_booking_is_submitted_by_the_user() throws Exception {
driver.findElement(By.id("firstname")).sendKeys("Behave Pro");
driver.findElement(By.id("lastname")).sendKeys("Rocks");
driver.findElement(By.id("checkin")).sendKeys("2018-01-01");
driver.findElement(By.id("checkout")).sendKeys("2018-01-03");
driver.findElement(By.id("submit")).click();
}
@Then("^the booking is successfully stored$")
public void the_booking_is_successfully_stored() throws Exception {
String bookingEntry = driver.findElements(By.cssSelector("tr")).get(1).getText();
assertThat("Behave Pro Rocks 2018-01-01 2018-01-03", is(bookingEntry));
driver.close();
}
With our code updated, let's run it with mvn test
to receive an error message similar to the one below:
The run fails because it cannot find the feature we want to the test to operate. Meaning that we are ready to build our production code to make the test pass.
Making our acceptance test pass
With our failing automated acceptance test in place we can now write production code to make the test pass. So let's create an index.html
file and paste in the following code and save:
<html>
<head>
<script>
addBooking=function(){var e=document.getElementById("bookings").insertRow(1);e.insertCell(0).innerHTML=document.getElementById("firstname").value,e.insertCell(1).innerHTML=document.getElementById("lastname").value,e.insertCell(2).innerHTML=document.getElementById("checkin").value,e.insertCell(3).innerHTML=document.getElementById("checkout").value};
</script>
</head>
<body>
<table id="bookings">
<tr>
<td>
<input type="text" id="firstname" placeholder="Firstname" />
</td>
<td>
<input type="text" id="lastname" placeholder="Lastname" />
</td>
<td>
<input type="text" id="checkin" placeholder="Check in" />
</td>
<td>
<input type="text" id="checkout" placeholder="Check out" />
</td>
<td>
<button id="submit" onclick="addBooking()">Add</button>
</td>
</tr>
</table>
</body>
</html>
With our production code created, update the driver.navigate.to()
method in our test to point to where the index.html
is located. Then finally run mvn test
one more time:
Congratulations, we now have a passing test!
Refactoring your code
Now that we have a passing green acceptance test we have a clear indication that:
We have completed the work we need to do
The feature we have created matches the described behaviour in captured scenario
In addition to this, we now have the capacity to refactor our production code in confidence. As we refactor we can run the green acceptance test and if it fails we can tell that our refactor is causing our feature to drift away from the expected behaviour defined in the scenario.
Going further
The example that you have just gone through is following a pattern within BDD that is known as Outside in Development. By creating a failing automated acceptance test a developer can comfortably create production code that makes the automated acceptance test pass. This guides the developer towards delivering the right thing for the business as well telling them that they are done. Finally, with passing tests in place, developers are free to refactor their code knowing if their refactor accidentally breaks something.
In this example we've given you a taste of what you can do with the Outside in Development approach and Behave Pro and there is a lot more to learn about the approach.
Further reading:
In addition to the theory there are many other practical tricks and approaches you can leverage to improve your automation, for example:
Learning more about Outside-in-development
Designing your Selenium-WebDriver code to become more re-usable and stable