Behavior-Driven Development: An Evolution in Software Design

[article]
Summary:
Behavior Driven Development (BDD) is not a revolution in software design, but rather an evolution in how we software engineers think about program design. In this article the author introduces Behavior-Driven Development, explore the rationale behind it, and introduce Open Source tools—some new, some familiar, that you can use to get you started.

Behavior Driven Development (BDD) is not a revolution in software design, but rather an evolution in how we software engineers think about program design. BDD was born from the observation that most developers were not realizing the full potentialof test-driven development (TDD). Many people say that TDD, done well, is not about testing but about design. However, the language of "testing" impedes novices and journeymen from realizing its full potential. In this article I will introduce Behavior Driven Development, explore the rationale behind it, and introduce Open Source tools—some new, some familiar, that you can use to get you started.

Extreme Programming (XP) popularized the concept of Test Driven Development a sa response to the reality that developers never seemed to have time to write unit tests. In response, taking developer testing to the extreme (pun intended), XP tells us to write tests before the code. The pattern: write a failing test; then write the code to satisfy that test; lather, rinse, and repeat.

As TDD practitioners have refined their art, the TDD community came to the realization that the real benefit of TDD it not about testing -- it is about design. Done well, TDD is a tool for creating supple, testable, and decoupled designs between collections of collaborating classes. However, the majority of developers practicing TDD have not made this mental leap. People started wondering why.

The Evolution of the TDD Developer
The path to enlightenment for a developer starting to practice TDD typically follows a pattern. [1] She,

    1. Starts using a unit testing framework; such as JUnit or NUnit.
    2. Starts gaining confidence in the code due to the existence of tests. Code can be refactored with impunity.
    3. Starts writing tests before the code.
    4. Realizes that tests, both hers an others, can be used as documentation for code.
    5. Begins writing tests first to design the class API.
    6. Realizes that by writing tests first, she is specifying the behavior of the class.
    7. Discovers the full potential of TDD by specifying interactions between collaborating objects by using mocks.

These last steps are truly the "Ah-ha!" moment of TDD.

Viewing the unimplemented class through the lens of a test, you realize that specifying the externally visible behavior of your class before the implementation allows you to define the behavior of your class. Definition of class behavior and interaction with collaborating classes is design. During this process questions can be asked about this class and its collaborators: Is this class doing too much? Too little? What are the externally visible behaviors of this class?

What impedes the developer from reaching this elevated level of software development? Two things. How we think about the problem of "testing" and the tools we use.

Language is the Leverage
Sapir and Whorf were American linguists in the lsquo;30s who developed the Sapir-Whorf Hypothesis. One of the principles of the hypotheses, the linguistic determinism principle, asserts that language determines the way we think, that is,

"There is a systematic relationship between the grammatical
categories of the language a person speaks and how that person both
understands the world and behaves in it."
[2]

Which is to say, the language we use affects how we think about the world.

This language of testing is given to us by the tools, namely xUnit. The classes we create are called 'Test' cases. Collections of test cases are 'Test' suites. Methods must begin with the word 'test.’ [3] The very word 'testing' implies an activity that happens last. From the dictionary 'testing' is defined as:

"To take measures to check the quality, performance, or
reliability of (something), esp. before putting it into widespread
use or practice.”
[4]

Within the body of test cases we make 'assertions' about our code. Assertions are defined as: 

"A confident and forceful statement of fact or belief.” [5]

The language of testing shapes our thought process into trying to answer the question "How does my class work, and is it right?" Instead we should be answering the question "What does my class do?" As Bob Martin has said, "Testing is about Specification, not Verification.” [6]

If I Only Had a Hammer!
Language this not the only place where our tools hinder our progress. Most integrated development environments that support xUnit provide some 'helpful' features for generating test cases, such as creating one test case per class and creating one test method for each (and every) method in a class.

Now these features can be helpful in the beginning when a developer is still new to the syntax of a test case. However, if (mis)taken as a best practice we learn bad testing habits.

Testing internal state and private methods are another bad habit. The externally visible behavior of a class has nothing to do with its internal state. Testing private methods is a test smell; it usually indicates that a class is doing too much and should be broken into several classes, each with their own behavior, tested independently.

Test organization can be a source of another test smell. As the number of test methods in test classes increases, it becomes too large to manage, maintain, and understand. A class may have behavior in several different scenarios, each scenario with many distinct behavior. Tests should be organized by functional context not simply by class name.

The language and tools of unit testing limit the activity of TDD to one of verification. BDD provides new tools, approaches, and language to approach software development from a different angle. One such BDD tool is rSpec, written in Ruby.

Behavior Driven Development Using rSpec
The rSpec project has been blazing the BDD trail by leveraging the flexibility of Ruby to create a domain specific language around the specification of behavior. [7] An example:

describe Stack do
before do
@stack = Stack.new
end

it "should be empty after construction" do 

@stack.length.should == 1 
end
end

Several things to note about this example; 

    1. rSpec requires that you describe what behavior is being expressed. You may no longer leave empty the descriptions passed to assert(). 
    2. We 'describe' a class, or a class in a given context. Specifically we describe the behavior of a class. 
    3. We use the word 'should' to specify class behavior. The choice of 'should' over 'shall' or 'must' is both subtle and important. It allows the reader of the spec to ask, "Well, should it?" 
    4. We again use the word 'should' when declaring our expected outcome. 

Compare the same example to a xUnit test (in this case Ruby's Test::Unit). 

class StackTestCase lt; TestUnit::TestCase
def setup
@stack = Stack.new
end

def test_empty_stack do 
assert_equal(0, @stack.length)
end
end

The mechanics of both examples are the same; setup the scenario and then declare some expectation. However, nothing in the latter example leads you to think about behavior. rSpec gives us the language to reason about the behavior of the class. 

rSpec, because of its Ruby underpinnings, has an advantage for language and syntactic flexibility. However, if you're not using Ruby, all is not lost. There are several BDD frameworks for both Java and .NET.

However, the easiest one to use is the testing framework you probably are already using, xUnit. Try this experiment on your next test case:

1. Name your test class based on one of the contexts, or scenarios, your class will execute in. For example, defining the behavior of a Stack class:

class AnEmptyStack_TestCase inherits TestCase {...} 

2. Put 'should' in your test method names along with a short description of intended behavior: 

public void test_shouldHaveOneElementWhenAddedTo() {} 

By changing the language you use to think about the classes you write you'll start to change how you think about the behavior of your classes. Describing only the externally observed behavior will help you stop testing internal state, stop testing private methods, and stop trying to validate your code. 

No fancy tools needed. 


About the author
Bob Cotton is a Test Architect at Rally Software Development. With over 12 years of developing and testing web applications, Bob has extensive experience testing applications using open source testing tools. From unit, load, functional, and GUI testing Bob has applied many tools and techniques to testing large scale, SaaS (Software as a Service) applications. Most recently at Rally, Bob has helped shape an Agile software methodology by including unique approaches to software test automation. Automated testing, using tools such as FitNesse and Selenium, is integrated into the development process from the beginning of each iteration. Prior to joining Rally, Bob worked as the System Architect at SynXis Corp, where he helped build and test a high-volume hotel reservation system. Bob holds a B.S. in computer science from George Mason University.


[1] See {link broken] - www. behaviour-driven.org/Introduction

[2] See Wikipedia http://en.wikipedia.org/wiki/Sapir%E2%80%93Whorf_hypothesis 2007-08-24

[3] This is changing with newer test tools, such as JUnit 4 and TestNG.

[4] Dictionary.app Version 1.0.1. Apple OS X

[5] Dictionary.app. Version 1.0.1. Apple OS X

[6] From a presentation given to the Chicago Process Improvement Network, March 5, 2003.

[7] http://rspec.rubyforge.org/

About the author

AgileConnection is a TechWell community.

Through conferences, training, consulting, and online resources, TechWell helps you develop and deliver great software every day.