Behavior-Driven Development: A Comprehensive Intro and Guide
Reading time 13 minutes
Behavior-driven development (BDD) is several things: a language for defining the behavior of an application; a collaboration tool for engineers, testers, and product managers; and a system for the automated testing of those behaviors. Created by Dan North over a decade ago, BDD emerged from the then-new world of agile software development practices as a means to be an easier introduction to test-driven development (TDD). In this post, I’d like to cover a few points, including what BDD is, the benefits it provides, and some guidance on how to introduce it to your team.
BDD, What Is It?
This is not a complete picture, but let’s start with the simple definition that BDD is a form of software acceptance testing with some distinguishing characteristics. It’s software testing that focuses on how the software should behave rather than how that behavior is implemented. Perhaps the most distinguishing characteristic is that BDD tests are written in common language such that they allow both technical and non-technical members of the team to describe the requirements of a software system. Let’s start by taking a look at that language. In order to do that, we’ll need a quick refresh on user stories.
First, User Stories
When Dan North was developing his concept of BDD in the early 2000s, the company he was working at was already describing features as user stories. If you aren’t familiar with user stories, they’re the definition of a feature of a software system from the perspective of a user of that system. A user story is written in a format that looks like this:
Delivering enterprise software is complex. Plutora’s Value Stream Management Platform makes it easy.
As a type of user
I want some feature
So that I can achieve some outcome
Let’s see what an example of this in practice might look like if you were overseeing the creation of a to-do style application. Maybe you have a persona of a busy adult who needs to find a way to better manage their responsibilities.
As a busy adult
I want to maintain a list of tasks
So that I can visualize the work I need to do
Given, When, Then
This style of writing is easy for everyone to understand, it doesn’t contain implementation details, and it highlights why a feature is important for a given persona. The user story format inspired the BDD format. A BDD test is written like this:
Given some context
When some action or event occurs
Then some outcome occurs
To go back to our to-do list application, let’s imagine we’re working on that user story to maintain a list of to-do items. Here’s how that might look with a BDD test:
Given I’ve logged in to the to-do app
When I type in a to-do item
And I hit the “enter” key
Then the new item is added to a list of to-do items
There are several cool technologies, such as Cucumber, that allow nontechnical folks to write test cases in this language and will generate a test that will pass once that feature is implemented. The language allows members of the team to collaborate and use the acceptance criteria of stories as automated, plain-language tests. While the language is an important part of BDD, we might think of it as just the “behavior” part. What about the “driven development” part?
Red, Green, Refactor
To understand the “driven development” aspect, we need to briefly look at TDD. TDD is a software development practice that uses tests as the basis for designing good software. The process for TDD (and BDD by association) is what’s referred to as the red, green, refactor cycle:
- Write a test for a feature that isn’t yet implemented in your application.
- Run the test; it should fail (red).
- Make the test pass as quickly as possible (green).
- Clean up the design of the implementation and reduce duplication (refactor).
The TDD process is centered around quick feedback loops where the engineer frequently returns to a state of working software evidenced by passing tests. BDD evolved from TDD at least in part to make the test-driving process more accessible to new agile teams. The important aspects of TDD that remain in BDD are the emphasis on feedback loops, automated testing, and writing failing tests before the implementation of a feature.
We started out with the simple definition that BDD is a form of software acceptance testing. What we’ve seen is that it’s really a practice.
- Scenarios emerge from well-defined features.
- Those scenarios can and ideally should be written in collaboration between engineers and business folks.
- The created scenarios are the basis for a failing test in the red, green, refactor feedback cycle.
- The tests pass and give evidence of working software.
Cool, so that’s what it is, but how exactly does it help your team?
How Will BDD Help My Team?
BDD offers several benefits to teams. Like many agile practices, it helps eliminate waste. Here are some of the benefits BDD gives your team:
- A suite of tests demonstrating that features are working
- Less rework over misinterpreted requirements or acceptance criteria
- A more proactive approach to testing for your development and testing team
Let’s examine each of these in a little more detail.
A BDD Test Suite
If your development team isn’t currently writing tests or is only doing a moderate amount of testing after feature development, a likely scenario is that the engineers on your team are less confident making changes to your application. If they don’t have a suite of tests they can run that ensure the system still works, they have a few options to ensure the integrity of the application when developing new features:
- Don’t verify that the changes have broken anything, only manually test their feature.
- Manually test the application while developing the feature to do a spot check.
- “Throw it over the wall” to the testing team; after all, that’s why they’re there, isn’t it?
The first option doesn’t protect from regressions, and it involves a lot of manual verification that a feature works. The second relies on that engineer’s understanding of the rest of the system and their personal bias on what to check and not check. Additionally, they’re probably spending a lot of time manually checking and rechecking features. The third option increases the feedback loop by queuing up work for the testing team that then trickles back to the development team in the form of unmet requirements, and the process starts again. However, now when these engineers revisit the features for rework from the testing or acceptance team, they’re likely to have forgotten some of the context from their initial work and introduce waste by relearning and reworking the problem at hand. Waste is the unifying factor here.
Tests as a Means to Eliminate Waste
Tests eliminate waste by giving engineers and testers confidence. They’ll have confidence that the following will happen:
- The feature they’re working on is done.
- They haven’t broken other features.
- Their tests are a replacement for wasting time manually verifying each feature.
Adopting BDD, similarly to adopting TDD, helps give your team confidence in the form of a test suite. BDD, because of the focus on behavior and consistent language, is easier for new teams to adopt. So if your team isn’t currently using tests to grow the software, this is one benefit that adopting BDD will give you.
Clearly Communicate Requirements
While the test suite improves team confidence, the creation of the tests can be a powerful tool to improve the communication between the technical and product people on your team. In BDD, the acceptance criteria of a story become the test. In our earlier example, we had a feature that stated our busy businessperson persona needed a list of their to-do items:
Given I’ve logged in to the to-do app
When I type in a to-do item
And I hit the “enter” key
Then the new item is added to a list of to-do items
It’s very closely related to, if not nearly word for word, what you would expect to see as acceptance criteria to such a story. BDD provides a framework to create a conversation between the business folks on your team and the engineers. Let’s assume for a minute that we create this test at the start of feature development for this user story. The process for coming up with this test could look something like this:
- The product folks on the team come up with a story that has clearly defined acceptance criteria.
- They discuss the acceptance criteria with the engineering team.
- Representatives from your engineering and testing teams sit down with a business analyst or product manager and write the test together.
Collaboration as a Means to Eliminate Waste
The goal is to eliminate waste by reducing the number of assumptions each group makes about what’s required for the feature to be finished. The engineering team and the product team are communicating and have created an automated definition for when the feature is complete. We waste less time revisiting code that was implemented without a clear understanding of the acceptance criteria. Creating these tests earlier in the development process also reduces testing time.
Pull Testing Up
It’s not uncommon for the relationship between a testing group and development group to be burdened by waste. If your organization has manual testers, this can mean that every time a feature gets submitted for review, they’ll run through an entire manual testing suite, much of which is unrelated to the feature being developed. Every time the feature is denied due to regression or incompleteness, this manual process starts again, duplicating the waste. This is on top of the wasted time between an engineer submitting a feature for testing and the time that it actually gets tested.
In BDD, we start testing much earlier in the process. Testers can participate in the creation of the test case along with engineers and business analysts. Similar to the time savings for engineers, testers can benefit from the automation of test scenarios and can focus their efforts on parts of the app that may be more difficult to test or can start to participate more actively in the development process.
The sooner we can definitively determine whether our software is working or not working, the sooner we can ship it or fix it. There’s no sooner way to tell than by starting development with a test.
Alright, so we can improve our engineers’ confidence to eliminate waste. We can improve the communication among the engineering team, testing team, and product teams to eliminate waste. We can also improve feedback times by pulling testing up in the process to, you guessed it, eliminate waste. These are some of the benefits you get by practicing BDD. But how do you go about introducing BDD into your team?
Behaviors, processes, and norms are difficult to change. BDD affects all of these things and isn’t a sweeping change you can expect to implement in a short period of time. I believe changes like this that impact a team’s flow and norms should be treated as experiments, should start small, and should be frequently measured. The current state of your team should be taken into account when defining what success looks like. If your team is fragmented, has no testing, and releases software infrequently, then simply getting more collaboration on the team might be a great first outcome. There’s no one-size-fits-all adoption method or definition of success.
Define the Outcomes You Want
Adopting BDD might be an attempt to solve a number of different problems. Maybe you want to reduce defects, improve cycle time, or make it easier for new engineers to onboard to your project. Pick something measurable. Defect rates or feature turnaround times are easy to measure compared to something like measuring code quality.
Automated tests will be more valuable if they fail your build process. The test suite should be included in your continuous integration steps. If you aren’t doing continuous integration, that’s a more important problem to solve than adopting BDD. When the tests fail, it should be apparent that they’re failing to everyone on the team. An information radiator, such as a large monitor, should show the current status of your test suite to your team.
A decision that affects the team should be made with the team. The team is more likely to take ownership of a practice that everyone believes can help them work more efficiently. Help the team own the solution.
And then start smaller. Your team’s current situation and context matter. If you’re considering introducing BDD on a large application, your first test is going to be difficult to write. Success might be that, even without an automated test, you get representatives from all the facets of your team in a room to transform success criteria into the given, when, then format.
You could progress to the point where the team is test-driving each feature by progressively adopting BDD. If you want to progressively adopt it, you’ll want to start by changing the way teams think about features. The first thing to work on would be to ensure that every feature, before it’s worked on by the team, has well-defined acceptance criteria. Once this is a consistent quality on the features your team is working on, it’s not that much of a stretch to get them to start creating the given, when, then scenarios when starting a feature.
Even if they’re just writing them out but not creating tests, you want them to be thinking this way. Similarly, from this point, it’s not much of a stretch to get your first actual test implemented in the system. It might even be going back to write a test for a feature that already exists, like a login feature. Make small adjustments and course correct.
Continuously Improve, Eliminate Waste
We’ve covered a lot of ground concerning BDD. As with any other agile practice, it’s always best to look for bottlenecks in your system and find the right place to make continuous improvements. If you want to make some large changes quickly, then perhaps you could take a look at Plutora’s value stream management platform that helps you build, test, and deploy your new features. Above all, be pragmatic. Eliminate waste, improve feedback loops, improve communication among members of your team, and foster an environment for continuous improvement.