DaedTech

Stories about Software

By

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)
    {
        if (!_productStockChecker.DoWeHaveInStock(individualLineItem.Product))
            return false;
    }
    return true;
}

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.

FigherJet

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.

  1. Descriptive, conversational names
  2. Expressions of acceptance criteria of the software
  3. 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.

5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
John Pazniokas
John Pazniokas
8 years ago

Did Infragistics do a redesign recently? One of the “I originally wrote this for..” sites used to have a tremendously painful design, with tiny, light-gray, single-spaced text. I had started clicking over to it, just to add a click-through on your blogs, but I gave up and stayed over here, at your “I don’t hate my readers” design.

Looking forward to this series.

Erik Dietrich
8 years ago
Reply to  John Pazniokas

I appreciate the click-throughs! They might have tweaked their site a bit, though it doesn’t really look different to me. I’m kind of curious now to go back through some of the posts and find that (though, hopefully, I won’t if it’s been improved wherever it was)

Ben Berry
Ben Berry
8 years ago

“To understand the how we must first take a small detour back in time”

This line seems remarkably apt, considering how much the “taste” of your code snippet reminds me of COBOL. 🙂

(And I for one don’t consider that a bad thing – we wouldn’t still have all those billions of lines of COBOL in play if newcomers couldn’t read and understand it).

Erik Dietrich
8 years ago
Reply to  Ben Berry

I’ve never worked with COBOL myself, but my understanding of it is that one of the main goals was to allow business language to be expressed in code. As I understand, it was designed to be readable by domain experts…?

It is kind of interesting to stop and think that this was a first class goal a long time ago, and we’re still struggling to get much more recent languages to have this property.

Ben Berry
Ben Berry
8 years ago
Reply to  Erik Dietrich

The way I see it, COBOL has been successful BECAUSE of its wordiness and readability. We all hated long variable names back in the days when we had to manually type them out in full on a mainframe terminal (and manually manage our linebreaks at column #77), but that’s no longer an issue with a modern code-completing editor.

In government and old-corporate environments it’s pretty common to have active COBOL code modules that were originally written 30+ years ago (and continuously modified since), but you don’t see much FORTRAN or ALGOL or PL/I from that era still in use…