BDD in .NET for Complete Initiates
Editorial Note: I originally wrote this post for the Infragistics blog. You can check out the original here at their site. Go on over there for content from me and a bunch of other authors as well.
It’s pretty likely that you’ve heard of behavior-driven development, or BDD. Maybe it’s just in the context of buzzword fatigue and wondering “how many different approaches to software have acronyms that end with DD?” Whatever your level of cynicism, or lack thereof, BDD is worth a look.
A lot of my work over the last few years has involved coaching and mentoring on the subject of writing clean code, and I often tell initially skeptical developers that they should be writing methods that BAs and managers could more or less read (in places pertaining to business logic, anyway). This isn’t as far-fetched as it sounds. Think of a bit of code that looked like this.
public bool IsCustomerOrderValid(CustomerOrder orderToBeEvaluated)
foreach(var individualLineItem in orderToBeEvaluated.LineItems)
Would it really be such a stretch to imagine a non-technical person being able to look at this and understand what was happening? Take an order to be evaluated, look through each of its line items, and check to see if the product they contain is in stock. You don’t need to be a programmer to have an idea of what’s happening here.
BDD From 10,000 Feet
BDD in essence, is taking this idea and expanding upon it by making domain-oriented conversation a part of software acceptance. Don’t worry about “how” just yet. Suffice it to say that you and various non-technical stakeholders can sit down together and write tests, in plain English, that can be run to demonstrate that system requirements are being met. That’s pretty powerful.
To understand the how we must first take a small detour back in time. BDD emerged as flavor of test driven development (TDD). In test driven development, each modification to the production code is driven by a failing unit test. This gave rise to a lot of tests with the spirit of (for instance), “when I pass null to this class constructor, it should throw a null argument exception” alongside of tests that expressed business purpose. TDD isn’t specific, per se, about the level of granularity of the tests that you write to drive production code modifications.
BDD emerged as an extension and narrowing of this process by having more preferences as to the nature of the tests. The tests themselves start to take on the following properties.
- Descriptive, conversational names
- Expressions of acceptance criteria of the software
- At a level of granularity that is meaningful to users/stakeholders of the software.
So now, there’s a framework where you drive all modifications to production code by describing, with an executable specification, a current shortcoming of the system. To bring this into the realm of specifics, consider this example of BDD that you’ll see a lot more of as time goes on. Let’s say that you’re working on a calculator app and that, so far, you’ve implemented addition, subtraction, and multiplication. Next up is division. But, remember, you don’t just open up your IDE and start hacking away at the production code. You first need a failing acceptance test to describe the system’s shortcomings.
Scenario: Regular numbers
* Given I have entered 3 into the calculator
* And I have pressed divide
* And I have entered 2 into the calculator
* When I press equal
* Then the result should be 1.5 on the screen
This is what your test looks like. There’s a test runner that understands how to parse this English, translate it into code in your domain, and execute it. So, at the moment you need to add the division feature, the first thing you do is describe what success looks like. This makes it a lot easier to keep your eyes on the prize, so to speak. So this approach isn’t some kind of purist approach to process, but a refreshingly pragmatic one.
How Does It Work?
You might have noticed that the English readable text I presented is conversational-ish. It’s a bit stilted with each statement starting with “Given” or “When” or what have you. That’s because it is written in a very readable language known as “Gherkin,” which is described as a “business readable, domain specific language.” Then, a test runner of sorts, known as “Cucumber,” executes these acceptance tests by parsing the Gherkin and mapping it to actual code that you’ve written to exercise your application.
To bring the concept home a little more concretely, you have to map the English to actual methods in your acceptance test code. So, you would have a scheme for binding the text following “Given” to a C# source method, such as via an attribute that contained the text “I have entered (.*) into the calculator.” This attribute would sit on top of a method that took an integer, x, as a parameter, and, in that method, you’d probably instantiate a Calculator and then call calculator.Press(x).
That’s really all there is to it. You write sentences in English that demonstrate to anyone interested how the system should work, and then you write code that expresses those sentences. The result is a series of executable tests that can do a pretty good job of describing what capabilities the system has and what capabilities are currently under construction or not working. The cynic might say that the biggest benefit is being able to tell project managers to stop asking for status and to go read the report of the test run. The optimist would say that the biggest benefit is closing the gap between technical and non-technical stakeholders in terms of understanding the system’s capabilities.
They’re both right.
Come back for the next post in the series, where I’ll show you how to get started doing this, from scratch, in a .NET code base.