Maximilian Wallisch
Behavior Driven Development (BDD) is a method invented by Dan North that focuses on describing application behavior in a formalized notation using concrete examples. When it comes to implementing BDD in a real project, it has many advantages but also some pitfalls. I had the opportunity to gain experience with BDD in a couple of projects. In this blog, I will share my thoughts and learnings from one such project on how to approach applying behavior driven development in an agile software development team.
An overview of the project
I was a test manager in the project. Our goal was to modernize a web portal through service-oriented architecture. Independent services were grouped into modules to enable functionality that was used by the business processes.
For developing and testing such services, we decided to use BDD methods. We did not implement BDD by the book – it was a selection of some core aspects that Dan North describes, that worked well for us as a team.
The following learnings and explanations do not, by any means, describe a perfectly executed implementation of BDD. These are just my opinions, based on my experience in this project.
The rationale behind implementing BDD
To implement the required modules and services, we chose an iterative and agile approach with a 2-week delivery cycle. This meant we had a steady flow of stories within the team, where one story usually described the scope of one service to be developed.
We used behavior driven development to:
- Clearly illustrate the application behavior by describing specific use cases
- Create a shared understanding of the requirements within the project team
- Cover the acceptance criteria with BDD scenarios
- Determine a basis for test automation
After a couple of sprints, we identified various useful practices and methods that worked in our project. Since a detailed description of our approach would be too much to share in a blog, I’d rather focus on some of the key aspects and the learnings. If you are interested in knowing more, feel free to contact us at Nagarro.
Ten eyes see more than two
One topic, many perspectives - that's what matters
The suggested “Given, When, Then” syntax for behavior driven development helps the team to explicitly think about the behavior of a system or the desired outcome of a described functionality. During our story refinement sessions where someone from every role (tester, developer, analyst, product owner, software architect, etc.) was present, we quickly realized that BDD enabled us to create a common understanding about the requirement. This also benefited us in strengthening the confidence in the story’s purpose and in understanding user requirements.
Test data availability
When describing the scenarios as a tester or a test manager, it is important to consider the test data you need for execution. If possible, try to include real and anonymized data from a test or production system.
Later, this helps with implementing the scenarios as automated test cases and integrating them into the test environment. For example, a test that deals with test data for a certain shipping address like “Test street 7, Test City” might be easy to integrate on a stage that uses mockups but will never run on a stage that uses real data.
Including the business or end-user in the process of creating or reviewing the BDD scenarios might uncover some edge cases or special data constellations that could be covered with tests too.
By defining the necessary test data, we often stumbled upon cases and scenarios that were not covered in the description of the story. Dealing with specific date/time formats for different countries is just one such example.
Scenario categorization
It is useful to tag the scenarios right away when creating them, so you know at first glance what type of scenario you are dealing with (e.g. @Regression, @Smoketest, @System-Integration, etc).
Doing so will enable you to find certain tests more easily and group them together to execute them in one run, especially if they are automated. For example, after deployment, all tests with the category “Smoketest” should pass before engaging in any further test activities.
Keep the prerequisites to a minimum
One of the most important learnings during the project was to keep the scenario outline (which describes the scope of the scenario) and the “Given” blocks as small as possible. Otherwise, dealing with and automating the scenarios quickly becomes very complex and cluttered.
If the prerequisites of a scenario are getting out of hand, it is often better to split the scenario into smaller chunks, thereby reducing the prerequisites per scenario.
Too big or too complex scenarios can reflect the story’s quality and size. Usually, if the story description is not detailed or comprehensive enough, the scenarios tend to be inaccurate. A good rule of thumb, is to split a story if it has more than 8-10 acceptance criteria.
Too many conditions? Too complex? The key is to focus on the behavior!
Manage the complexity
With highly integrated software (comprising a lot of connections to third-party systems), the scenarios naturally become complex. The external systems need to be up and running, and the test data gets manipulated by a lot of other systems before being tested or validated. Story-splitting can be a solution here, where we have only the minimal, necessary connections to an external system covered in one story.
Another learning was about the importance of focusing entirely on the behavior of the system under test. We do not need to describe the behavior of external systems in the scenarios – sometimes, our scenarios also went on to cover external system behavior, when all that was needed was a simple input or output from the system!
Even if you try hard to manage the complexity, some stories are complex by nature – in that case, BDD helps in at least arriving at a common understanding as a team, by discussing the scenarios together.
Another thing with complexity is that it is a risk for test automation. If there are a lot of dependencies, it is difficult to run the tests reliably and in isolation. You can try to use mock or service-virtualization to mitigate that risk. But, at the same time, you might find data-related errors only at a later stage in testing, when the tests are run on a stage with integrated real services.
Keeping a consistent notation
While the notation or syntax of behavior driven development is quite clear, there are cases where you have multiple possibilities to describe certain constellations of your scenarios.
For example, one scenario could look something like this:
Scenario: Edit Size in ‘Choose Size’ UI
Given I am on the “Choose Size“ tab in the UI
And the Size is 0
When I click the “Edit“ Button
And I click in the Input field
And I enter the value “50“
And I click the save Button
Then the new Size should be 50
This description is very detailed and relies heavily on UI elements such as “button” or “input field”. It is also more of a description of the process instead of the behavior. Keeping it like this would mean a lot of maintenance effort if some UI buttons change or if the functionality moves to another tab in the UI.
The following can be a better approach:
Scenario: User wants to edit Size
Given the Size is <initial>
When the User changes the Size to <newSize>
Then the new Size should be <ResultSize>
Example:
Initial |
newSize |
ResultSize |
0 |
50 |
50 |
50 |
20 |
20 |
20 |
0 |
0 |
In this case, it doesn’t matter what the buttons are called or where to find the functionality – it’s just about the behavior that is illustrated with three examples. The behavior will be the same regardless of any implementation details that are described in the story.
In the project, it took everyone a few sprints to think on a more abstract level that only focuses on behavior, as we constantly reviewed the scenarios and agreed upon a common notation.
Define the responsibilities
The last insight that I’d like to share is that BDD helps a lot in understanding the responsibilities of various roles in a software development team. In this project, we had a distributed team, which sometimes makes communication a little tricky. With BDD, you can derive tasks for different team members as per the scenarios, thus making things clearer.
For example:
- the tester is responsible for providing test data
- the developer or automation engineer is responsible for implementing the scenario as code
- the product owner takes care of getting input on edge cases from the users
- the software architect ensures that any functional gaps discovered in the requirements are considered in a new service
Conclusion
Behavior driven development helped a lot in increasing the understanding between team members, especially when it came to having clarity about different responsibilities and discussions about the scenarios among the different roles. Starting out with BDD can be a bit daunting, but sticking to it really pays off, provided you always adapt and improve how you use it as a team.
For me, the most prominent benefit that BDD has to offer is that it encourages and requires communication and coordination within the team, thus ensuring high quality right from the beginning of the project.