This is a guest posting by Justin Rohrman.
Behavior-driven development (BDD) is part of the development process in my current gig. I had lightly used BDD frameworks years before, but never the process, and we hadn’t heavily integrated it into our development cycles. I spent some time researching, hunting for authentic stories about using the process and tools, but I only found definitions and information about programming libraries. I couldn’t find an in-depth experience report.
I want to start writing that now, this is what I have found.
Get TestRail FREE for 30 days!
BDD provides a simple framework for describing state, transitions, and outcomes through a given-when-then convention. These three words are the focus of BDD. “Given-when-then” is a tool to frame and constrain how conversations in software projects happen. Yes, constrain — my experience has been counter to what most BDD leaders promote.
My experience with the conversation aspect of BDD is very similar to my experience with Scrum. These frameworks are where you begin if you are at the beginning of developing a team. If you are starting from nothing, your technical team doesn’t know how to talk about product usage, so BDD is a place to start. But once technical teams are accustomed to talking about customer value, how the product is used, and how we might test to discover problems, then it is probably time to break the convention and discover what works for the team.
If having conversations is more important than anything else that happens in the context of BDD, I want to have conversations.
We initially started using BDD by talking through the given-when-then framework. This convention allowed us to talk about the specific aspects of a change that a customer values:
- Given I am on a new form
- When I populate a signature field
- Then all fields on the form become disabled
This small bit of text helps us think about what a person wants to do with our software, but it also sets off my tester alarms. Testing, to me, is an open-ended activity. Questions immediately pop into my head when I see a new page, or even a new field on a page. In BDD terms, we might say:
- Given I am on my user profile
- When I update my birthdate
- Then my birthdate is displayed under my avatar
Without the BDD framework, I automatically start thinking, what is a date? What is a good date? What happens when I enter a bad date? Can I make the date display under the avatar “misbehave” somehow? I have never seen BDD as a development practice capture these questions.
What I do see is anchoring on simple tests or demonstration scenarios. We start working on a change with a stubbed BDD scenario, get to the point where that scenario passes, and then we may think we are done. If we’re not careful, BDD can make it easy to forget that there are a lot of unanswered questions we should be asking.
While I often hear stories about tests being built as a questioning framework, we build ours as bits of automation during a development cycle.
One of the strong points of my current team is that the question “How do we test this?” is often one of the first things we ask. This leads to more careful design and more testable (and usable) software. So, when we start a new change with an important UI component, we will often start by building a BDD test.
This begins by talking through the change: what we are doing, what the customer is expecting to get out of this, what should be done at the unit level, and what is best left to test in the browser. Most often, we settle on one or two scenarios to build against the browser using code.
Let’s use my birthdate example once more. I would start by thinking about the state I need the software to be in to test this, and also whether there is code I can reuse — or, rather, whether there is a step, and maybe a bit of page object, that already exists that we can use so we don’t have to write new code. At this point in the life of our test suite, the answer is often yes.
I take the step that defines my initial state, the “given” part of the scenario, and move from there. The next questions are “What actions do I want to perform?” and “What assertions do I want to make?” Because we have a new date field, I probably have to update my page object to define that field and make it available for tests. If I want to develop my test with customer value in mind, I’ll start from the outside in by creating the plain-text step first: when I set a birth-date.
After specifying what I want to do, I’d open my step file and create a new step that sets a value in the date field. At this point you have a choice to make: do things the easy way and set a value in the DOM, or do things the realistic way and use the date picker. I want realism, so I write a method to open the datepicker and set a value.
Once we can manipulate data, we want to make an assertion that the data persists. In our case, we want to do that in two places: once in the date field, to make sure that our selected date displays, and once where the user avatar displays.
If I had a chance to rebuild this framework from the ground up, I would completely dispense with Cucumber. The notion that nontechnical members of the development team, such as product owners, will write and review tests using Cucumber plain-text syntax is approaching absurd. For me, Cucumber adds an extra layer or two of abstraction, namely the feature files and the step files. Doing away with those would make a more maintainable test framework that most slightly technical people would be able to use and understand.
I will say, though, that having the steps in plain text makes tracking failures in continuous integration easier. Rather than seeing a stack trace and a questionable line number, I can see that a test failed on “When I set a birth-date.”
BDD practitioners regularly tell me that the most important part of these tests is the conversation, but at this point, I’m not sure I agree.
I work in an environment where we make small changes and deliveries very frequently. We work in pairs and can deliver small changes to production every two to three days, on average. We are also a refactoring machine. Improving the code, whether test code or production code, is part of the ethos. That translates to a lot of code, as well as new risk being introduced frequently, however small a change may be. Once we make a change and have passing tests at appropriate levels in the tech stack — unit, service and browser — we take a look at what can be refactored. That usually means breaking something that worked before. Having those layers of tests, including the UI, will often tell us we have gone astray before we make a build and can perform exploration.
I see BDD tests fail often — maybe not daily, but often enough for the value to be completely obvious. We like to pretend that we can understand every state a piece of software can get into — fields will only ever have these values, the customer will only ever follow these workflows — and we are regularly surprised. It is hard to predict how a change will affect other parts of our product. Tests built with BDD help us discover these problems quickly.
My experience in BDD is different from many of the stories I hear, as are the things I value about it. But I enjoy it and appreciate it as part of my testing practice. BDD helps us keep customer value in mind, drives simple code design, and provides a fast feedback loop for refactoring and future changes.
How are you using BDD?
This is a guest posting by Justin Rohrman. Justin has been a professional software tester in various capacities since 2005. In his current role, Justin is a consulting software tester and writer working with Excelon Development. Outside of work, he is currently serving on the Association For Software Testing Board of Directors as President helping to facilitate and develop various projects.
Test Automation – Anywhere, Anytime