Getting Started with Behavior-Driven Development
Editorial note: I originally wrote this post for the TechTown blog. You can check it out here, at their site. While you’re there, have a look around at the different training courses they offer.
You’ve probably heard of behavior-driven development (BDD). However, if you’ve never practiced it, you may perceive it as one of many in a nebulous cloud of acronyms. We have BDD, TDD, DDD, and ATDD. All of these have a “D” standing for “driven” and another one standing for either “development” or “design.” Apparently, we software developers really like things to drive us.
I won’t engage in a full “DD” taxonomy here, as this post concerns itself with behavior-driven development only. But we will need to take a tour through one of these in order to understand BDD’s motivations and backstory.
Behavior-Driven Development Origins and Motivation
To understand BDD, we must first understand test-driven development (TDD). Luckily, I wrote a recent primer on that. To recap briefly, TDD calls for you to address every new thing you want your production code to do by first writing a failing test. Doing this both verifies that the system currently lacks the needed functionality and gives you a way to later know that you’ve successfully implemented it.
With TDD, you deal in microtests. These distinguish themselves by being quite specific and granular. For instance, you might assert that you get a null reference exception when invoking a method with a null parameter. You’ll pardon non-technical project stakeholders for a distinct lack of interest in these microtests.
BDD evolved from asking the question, “Why don’t we do this for tests that the business might care about?” It follows the same philosophical approach and logic. But instead of worrying about null parameters and exceptions, these tests address the system’s behavior at the capability or feature level.
Behavior-driven development follows the TDD cadence: express a current system deficiency with a failing test. But this time the failing test is, for example, when I deposit money into my checking account, I can see the reflected balance. Work then proceeds on that feature until the test passes. At this time, the team considers the card complete.
Speaking the Language of the Business
The first thing you probably find yourself wondering is how this works from a nuts and bolts perspective. Sure, it makes for a great concept. But how exactly do you translate businesspeople’s English into code? Enter a clever construct called Gherkin.
Gherkin operates via a simple but clever premise—restrict English to a very narrow, and thus parsable, subset. Let’s look at an example.
Given a checking account balance of $100 When I deposit $50 Then I should see a balance of $150
This won’t exactly win a Pulitzer Prize, but programmers and business folks alike can easily read and understand it. And for this compromise in eloquence, you get the kind of pattern that makes programming languages work. “Given,” “when,” and “then” all represent meaningful language keywords. A framework uses these to map the statements that come next to methods in source code.
And Then Translating Business Language to Code
To understand how that works, consider some C# code. This represents a relatively concrete example of mapping the statement following the given keyword to some actual, executable code in your codebase.
[Given(@"a checking account balance of \$(.*)")] public void GivenACheckingAccountBalanceOf(decimal amount) { var account = new CheckingAccount() { Balance = amount }; }
The first line of code contains a C# attribute (equivalent to a Java annotation, for you Java folks). This attribute links the English text to the method in question. As you can see, it also uses an escaped variable to translate the number 100 into a variable for the method.
From there, you have it pretty easy. The “given” stipulates a checking account balance of $100. So you oblige in the code of your domain by creating a checking account with a $100 balance. You would follow suit for implementations of the “when” and “then” keywords as well. All of these combine to translate simple English into tests in your code. And your BDD framework will have enough smarts to execute the methods in the proper order.
Now I should mention at this point that things aren’t quite this simple. You will need to manage some class-level state between invocations of these methods. In other words, in the method above, I declare a variable with only method scope. Obviously, you couldn’t do anything useful with that in the “when” and “then” methods. I’m just using this oversimplified code to demonstrate the business-language-to-code mapping.
Collaborating with Business Stakeholders
Now that you understand how this works conceptually, we can revisit how you approach this development paradigm. With TDD, you write a failing test, make it pass, and then refactor, all the while keeping all remaining tests green and passing. What kind of cadence do you adopt with BDD?
To understand that, think beyond the development team. This approach fits in pretty seamlessly with Scrum and its roles and stakeholders. For example, imagine a product backlog filled with user stories. Working with the product owner and any necessary analysts, you can pretty easily express a user story as one or more Gherkin scenarios. A so-called three amigos collaboration between BAs, developers, and testers can result in the writing of failing Gherkin scenarios.
The development team then gets them to pass. After that, the QA team verifies their passing, and the product owner or business stakeholders signify their acceptance of the effort.
As for the development team’s cadence with the scenarios, that also becomes simple. You have a backlog that prioritizes scenarios (stories) by business value. The team picks one up, expresses it with a red behavior/test, and gets that test to pass without breaking others. It echoes the TDD cadence.
The BDD/TDD Cycle
That brings up the obvious question of how to practice BDD and TDD simultaneously. People often wonder about this after receiving an introduction to the topic, and understandably so.
To clear the muddied waters, consider that these two practices occur at different levels of granularity. BDD operates at the granularity of features and components, whereas TDD operates at the granularity of methods. When you implement a feature in your codebase, you usually add or modify many different methods and classes. This means that TDD practitioners write many different microtests during feature development.
This idea translates naturally to the BDD/TDD cycle, as illustrated by the diagram in this post. You write a failing acceptance test/behavior first. You then enter into your TDD cycle, working on the feature with your normal red-green-refactor cadence. After some number of those cycles, you’ll finish the feature/behavior, resulting in a green acceptance test.
Getting Started on Your Tech Stack
While I used an example in C# earlier, I could have picked pretty much any language. I have this flexibility because of the language-agnostic nature of Gherkin. Quite simply, you can use Gherkin in just about any tech stack you please.
All you need is a framework for wiring the Gherkin to the tests in your language. If one doesn’t exist, you could always write one, but plenty of them exist. This post contains an impressive breakdown of your options, organized by tech stack. As you can see, you have plenty of options with major programming languages.
From here, your best bet is to pick one, install it, and try it out. I can speak from experience in saying that experimentation is your quickest path to getting comfortable. By no means have you completed your learning on the subject, but hopefully things have been demystified enough for you that you’re ready to try it out for yourself.
Great explanation about BDD, TDD’s higher-level cousin. In practice, you can do BDD without Gherkin – its not exactly the full-blown version, but you can start writing your tests from the outside-in as if you were practicing BDD with the business language (Gherkin) and framework in place. All you have to do is start with the business requirements just like you have there: //arrange var sut = new CheckingAccount(balance: 100.00m); //act sut.Deposit(50.00m); //assert Assert.AreEqual(150.00m, sut.Balance); The trick is that the CheckingAccount class drives all of the underlying implementation – whatever it is – and you just get the nice clean… Read more »
Good point. I’ve actually done similar things to what you’re describing at times — writing tests that drove through presentation layer types of classes in a conceptually similar way, but without the specific “formalism” of the framework or of Gherkin.
My experience of BDD has been quite negative. BAs were completely uninterested, and I understand why they wouldn’t be interested. Even though the Ghekin language is designed to be read easily, it still has strict syntax that can be a pain to get right. BAs are not concerned with automated tests passing, they are concerned with business requirements being met and stakeholders being satisfied. Even with BA buy-in I would be reluctant to try this again. We had buy-in from our test team and they would enthusiastically write lines and lines of Gherkin, only for it to fail to transpile… Read more »
That’s a much different experience than I’ve had. (Though I’ve never done what it sounds like you experienced, which is try to get testers or BAs to actually write the Gherkin, instead of just being party to it while or after developers wrote it. That doesn’t sound like it would go well.) As for the “uncritical” nature of my post, I’m not sure why you’d expect criticism of X in any article entitled, “Getting Started with X.” Given that you’ve taken your personal experience with the approach/tool and extrapolated that to the broad brush of adopters only doing it because… Read more »