Contents


In pursuit of code quality

Adventures in behavior-driven development

Isn't it time you learned how to JBehave?

Comments

Content series:

This content is part # of # in the series: In pursuit of code quality

Stay tuned for additional content in this series.

This content is part of the series:In pursuit of code quality

Stay tuned for additional content in this series.

Developer testing is clearly a good thing. Testing done early — say, as you write your code — is an even better thing, especially when it comes to code quality. Write your tests first and you win the blue ribbon. The added momentum of being able to examine the behavior of your code and debug it preemptively is undeniably high-speed.

Even knowing this, we're nowhere near the critical mass that would make writing tests before writing code a standard practice. Just as TDD was an evolutionary next step extending from Extreme Programming, which pushed unit-testing frameworks into the limelight, evolutionary leaps are waiting to be made from the foundation that is TDD. This month, I invite you to join me as I make the evolutionary leap from TDD to its slightly more intuitive cousin: behavior-driven development (BDD).

Behavior-driven development

While test-first programming works for some people, it doesn't work for everyone. For every application developer who avidly embraces TDD, there are many others who actively resist it. Even with the abundance of testing frameworks like TestNG, Selenium, and FEST, the reasons for not testing code are manifold.

Two common reasons for not doing TDD are "There's not enough time for testing" and "The code is too complex and hard to test." Another hurdle in test-first programming is the test-first concept itself. Many of us view testing as a tactile activity, more concrete than abstract. Experience tells us that it isn't possible to test something that doesn't already exist. For some developers, given this conceptual framework, the idea of testing first is an oxymoron.

But what if, rather than thinking in terms of writing tests and how to test things, you thought about behavior, instead? By behavior, I mean how an application should behave — in essence, its specification.

As it turns out, you already think this way. We all do. Watch.

Frank: What's a stack?

Linda: It's a data structure that collects objects in a first in, last out (or last in, first out) manner. It usually has an API with methods like push() and pop(). Sometimes you'll see a peek() method, as well.

Frank: What does push() do?

Linda: push() takes an input object, say foo, and places it into an internal container (like an array). push() usually doesn't return anything either.

Frank: What if I push() two things, like foo and then bar?

Linda: The second object, bar, should be on top of the conceptual stack (containing at least two objects), so that if you call pop(), bar should come off instead of the first object, which, in your case, is foo. If you called pop() again, then foo should be returned and the stack should be empty (assuming there wasn't anything in it before you added the two objects).

Frank: So pop removes the most recent item placed into the stack?

Linda: Yes, pop() should remove the top item (assuming there are items to remove). peek() follows the same rule, but the object isn't removed. peek() should leave the top item on the stack.

Frank: What if I call pop() without having pushed anything?

Linda: pop() should throw an exception indicating that nothing has been pushed yet.

Frank: What if I push()null?

Linda: The stack should throw an exception because null isn't a valid value to push().

Notice anything particular about this conversation (aside from the fact that Frank didn't major in computer science)? Nowhere was the word "test" used. The word "should" slipped in here and there quite naturally, however.

Doing what comes naturally

BDD isn't anything new or revolutionary. It's just an evolutionary offshoot of TDD in which the word "test" is replaced by the word "should." Semantics aside, many people find the concept of should a much more natural development driver than the concept of testing. Thinking in terms of behavior (shoulds) somehow paves the way into writing specification classes first, which, in turn, can be a very efficient implementation driver.

Using the conversation between Frank and Linda as a basis, let's see how BDD drives development in just the way that TDD was intended to promote.

JBehave

JBehave is a BDD framework for the Java™ platform inspired by the xUnit paradigm. As you can probably guess, JBehave stresses the word should, rather than test. Just like JUnit, you can run JBehave classes in your favorite IDE and via your preferred build platform, such as Ant.

JBehave lets me create a behavior class much like I would in JUnit; however, in the case of JBehave, I don't need to extend from any particular base class, and all my behavior methods need to start with should, rather than test), as shown in Listing 1.

Listing 1. A simple behavior class for a stack
public class StackBehavior {
 public void shouldThrowExceptionUponNullPush() throws Exception{}
 public void shouldThrowExceptionUponPopWithoutPush() throws Exception{}
 public void shouldPopPushedValue() throws Exception{}
 public void shouldPopSecondPushedValueFirst() throws Exception{}
 public void shouldLeaveValueOnStackAfterPeep() throws Exception{}
}

The methods defined in Listing 1 all start with should and they all create a human-readable sentence. The resulting StackBehavior class describes many of the features of the stack in the conversation between Frank and Linda.

For instance, Linda stated that a stack should throw an exception if a user attempted to place null onto it. Check out the first behavior method in the StackBehavior class: It's called shouldThrowExceptionUponNullPush(). The other methods follow the same pattern. This descriptive naming pattern (which is by no means unique to JBehave or BDD) makes it possible to state a failing behavior in a human readable manner, as you'll see shortly.

Speaking of shouldThrowExceptionUponNullPush(), how would you verify this behavior? It seems to me that you'd first need a push() method on a Stack class, which is easy to define.

Listing 2. A simple stack definition to facilitate exploring behavior
public class Stack<E> {
 public void push(E value) {}
}

As you can see, I've coded the minimum amount of a stack so I can start fleshing out the required behavior first. As Linda stated, the behavior is simple: If someone calls push() with a null value, the stack should throw an exception. Now look at how I've defined this behavior in Listing 3.

Listing 3. The stack should throw an exception if null is pushed
public void shouldThrowExceptionUponNullPush() throws Exception{
 final Stack<String> stStack = new Stack<String>();

 Ensure.throwsException(RuntimeException.class, new Block(){
   public void run() throws Exception {
    stStack.push(null);
   }
 });
}

Great expectations and overrides

A few things are happening in Listing 3 that are unique to JBehave, so let me explain. First, I create an instance of the Stack class and limit it to String types (via Java 5 generics). Next, I use JBehave's expectation framework to essentially model my desired behavior. The Ensure class is analogous to JUnit or TestNG's Assert type; however, it adds a series of methods that facilitate a more readable API (this is often called literate programming). In Listing 3, I've ensured that a RuntimeException will be thrown if push() with null is called.

JBehave also introduces a Block type, which is implemented by overriding the run() method with your desired behavior. Internally, JBehave ensures that your desired exception type isn't thrown (and, therefore, caught), a failure state is generated.

If I run the behavior from Listing 3 now, I should see a failure. As currently coded, the push() method doesn't do anything; so there is no way an exception will be generated, as you can see from the output in Listing 4.

Listing 4. The behavior I want isn't there
1) StackBehavior should throw exception upon null push:
VerificationException: Expected: 
object not null
but got: 
null:

The sentence in Listing 4, "StackBehavior should throw exception upon null push," mimics the behavior's name (shouldThrowExceptionUponNullPush()) along with the name of the class. Essentially, JBehave is reporting that it didn't get anything when it ran the desired behavior. Of course, my next step is to make that behavior pass, which I've done by checking for null in Listing 5.

Listing 5. Adding the specified behavior in the stack class
public void push(E value) {
  if(value == null){
   throw new RuntimeException("Can't push null");
  }
}

When I rerun my behavior, everything is good to go, as shown in Listing 6.

Listing 6. Success!
Time: 0.021s

Total: 1. Success!

Behavior drives development

Doesn't the output in Listing 6 look similar to JUnit's output? That's probably not a coincidence, is it? As mentioned, JBehave is modeled after the xUnit paradigm and even supports fixtures via setUp() and tearDown(). Given that I'm probably going to be using a Stack instance throughout my behavior class, I might as well push (no pun intended) that logic into a fixture, as I've done in Listing 7. Note that JBehave will follow the same fixture contract that JUnit follows — that is, it will run a setUp() and tearDown() for every behavior method.

Listing 7. Fixtures in JBehave
public class StackBehavior {
 private Stack<String> stStack;
  
 public void setUp() {
  this.stStack = new Stack<String>();
 }
 //...
}

Moving on to the next behavior method, shouldThrowExceptionUponPopWithoutPush() indicates I'll have to ensure similar behavior to that of shouldThrowExceptionUponNullPush() from Listing 3. As you can see in Listing 8, there isn't any particular magic going on — or is there?

Listing 8. Ensuring the behavior of pop
public void shouldThrowExceptionUponPopWithoutPush() throws Exception{
		
 Ensure.throwsException(RuntimeException.class, new Block() {
   public void run() throws Exception {
    stStack.pop();
   }
 });
}

As you've probably figured out, Listing 8 won't actually compile at this point because pop() hasn't been written yet. But before I start to do that (write pop()), let's think a few things through.

Ensuring behavior

Technically, I could just implement pop() to only throw an exception at this point, regardless of calling order. But going down this behavior route encourages me to think about an implementation that supports my desired specification. In this case, ensuring that pop() throws an exception if push() hasn't been called (or logically, if the stack is empty) means that the stack has a state. And as Linda mused earlier, a stack usually has an "internal container" that actually holds items. Accordingly, I can create an ArrayList for the Stack class that holds values passed into the push() method, as shown in Listing 9.

Listing 9. A stack needs an internal way to hold objects
public class Stack<E> {
 private ArrayList<E> list; 

 public Stack() {
  this.list = new ArrayList<E>();
 }
 //...
}

Now I can code the behavior for the pop() method, which ensures that if the stack is logically empty, an exception will be thrown.

Listing 10. Implementing pop just got easier
public E pop() {
 if(this.list.size() > 0){
  return null;
 }else{
  throw new RuntimeException("nothing to pop");
 }
}

When I run the behavior in Listing 8, things work as expected: Because the stack isn't holding any values (hence, its size isn't greater than zero), an exception is thrown.

The next behavior method is called shouldPopPushedValue(), which turns out to be easy to specify. I simply push() a value ("test") and ensure that when I call pop(), that same value is returned.

Listing 11. If you push a value, it should pop off, right?
public void shouldPopPushedValue() throws Exception{
 stStack.push("test");
 Ensure.that(stStack.pop(), m.is("test"));
}

Dial 'M' for Matcher

In Listing 11, I ensure that pop() returns the value "test". In the course of using JBehave's Ensure class, you'll often find that you need a richer way to specify expectations. JBehave meets this need by offering a Matcher type for implementing rich expectations. In my case, I chose to reuse JBehave's UsingMatchers type (the m variable in Listing 11) so I could use methods like is(), and(), or(), and a host of other neat mechanisms for building a more literate style of expectations.

The m variable from Listing 11 is a static member of the StackBehavior class, as shown in Listing 12.

Listing 12. UsingMatchers in the behavior class
private static final UsingMatchers m = new UsingMatchers(){};

With the new behavior method coded in Listing 11, it's time to run it — but doing so indicates a failure, as shown in Listing 13.

Listing 13. My newly coded behavior doesn't work
Failures: 1.

1) StackBehavior should pop pushed value:
java.lang.RuntimeException: nothing to pop

What happened? It turns out that my push() method wasn't finished. Back in Listing 5, I coded the bare minimum implementation to get my behavior to work. Now it's time to finish the job, by actually adding the pushed value into the internal container (if the value isn't null). I do this in Listing 14.

Listing 14. Wrap up that push method
public void push(E value) {
 if(value == null){
  throw new RuntimeException("Can't push null");
 }else{
  this.list.add(value);
 }
}

But wait — when I rerun the behavior, it still fails!

Listing 15. JBehave reports a null value instead of an exception
1) StackBehavior should pop pushed value:
VerificationException: Expected: 
same instance as <test>
but got: 
null:

At least the failure in Listing 15 is different from Listing 13. In this case, rather than an exception being thrown, the "test" value isn't being found; null is being popped. Looking closely at Listing 10 reveals the issue: I initially coded the pop() method to return null if the internal container had anything in it. Well, that's easy to fix.

Listing 16. Time to finish coding this pop method
public E pop() {
 if(this.list.size() > 0){
  return this.list.remove(this.list.size());
 }else{
  throw new RuntimeException("nothing to pop");
 }
}

But now if I rerun the behavior, I get a new failure.

Listing 17. Yet another failure
1) StackBehavior should pop pushed value:
java.lang.IndexOutOfBoundsException: Index: 1, Size: 1

A close reading of the information in Listing 17 uncovers the issue: I need to account for 0 when dealing with an ArrayList.

Listing 18. Accounting for 0 fixes the issue
public E pop() {
 if(this.list.size() > 0){
  return this.list.remove(this.list.size()-1);
 }else{
  throw new RuntimeException("Nothing to pop");
 }
}

The logic of stacks

Thus far, I've managed to implement push() and pop() in such a manner as to permit a number of behavior methods to pass. I've yet to tackle the meat of the stack, however, which is the logic associated with multiple push()es and pop()s, along with throwing in an occasional peek().

First, I'll ensure that the basic algorithm of my stack (first in, last out) is sound, via the shouldPopSecondPushedValueFirst() behavior.

Listing 19. Ensuring typical stack logic
public void shouldPopSecondPushedValueFirst() throws Exception{
 stStack.push("test 1");
 stStack.push("test 2");
 Ensure.that(stStack.pop(), m.is("test 2"));
}

The code in Listing 19 works as planned, so I'll implement another behavior method (in Listing 20) to ensure that using pop() twice shows proper behavior, as well.

Listing 20. Going deeper with stack behavior
public void shouldPopValuesInReverseOrder() throws Exception{
 stStack.push("test 1");
 stStack.push("test 2");
 Ensure.that(stStack.pop(), m.is("test 2"));
 Ensure.that(stStack.pop(), m.is("test 1"));
}

Moving on, I'd like to ensure that peek() works as intended. As Linda said, peek() follows the same rules as pop(), but "should leave the top item on the stack." Accordingly, I've implemented the behavior for the shouldLeaveValueOnStackAfterPeep() method in Listing 21.

Listing 21. Ensuring that peek leaves the top item on the stack
public void shouldLeaveValueOnStackAfterPeep() throws Exception{
 stStack.push("test 1");
 stStack.push("test 2");
 Ensure.that(stStack.peek(), m.is("test 2"));
 Ensure.that(stStack.pop(), m.is("test 2"));
}

Because peek() hasn't been defined yet, the code in Listing 21 won't compile. In Listing 22, I've defined a bare-bones implementation of peek().

Listing 22. Peek is required, of course
public E peek() {
 return null;
}

Now the StackBehavior class will compile, but it still won't run.

Listing 23. No surprise that null was returned, right?
1) StackBehavior should leave value on stack after peep:
VerificationException: Expected: 
same instance as <test 2>
but got: 
null:

Logically, peek() doesn't remove the item from the internal collection, it basically just passes a pointer to it. Consequently, I use the get() method on ArrayList, rather than remove(), as shown in Listing 24.

Listing 24. Don't remove it
public E peek() {
 return this.list.get(this.list.size()-1);
}

Nothing to see here

Now rerunning the behavior from Listing 21 yields a passing grade. Doing this exercise has revealed an issue, however: What is the behavior of peek() if nothing is there? If a pop() with nothing in it should throw an exception, should peek() do the same?

Linda didn't say anything about this, so apparently, I need to flesh out some new behavior myself. In Listing 25, I've coded the scenario for "What happens if peek() is called without a push()."

Listing 25. What happens if peek is called without a push?
public void shouldReturnNullOnPeekWithoutPush() throws Exception{
 Ensure.that(stStack.peek(), m.is(null));
}

Once again, no surprises here. Things blew up, as you can see in Listing 26.

Listing 26. It's nothing to peek at
1) StackBehavior should return null on peek without push:
java.lang.ArrayIndexOutOfBoundsException: -1

The logic to fix the defect is quite similar to the logic in pop(), as you can see in Listing 27.

Listing 27. This peek() needs some fixing
public E peek() {
 if(this.list.size() > 0){
  return this.list.get(this.list.size()-1);
 }else{
  return null;
 }
}

All my modifications and fixes to the Stack class result in the code you see in Listing 28.

Listing 28. Ah, a working stack
import java.util.ArrayList;

public class Stack<E> {

 private ArrayList<E> list;

 public Stack() {
  this.list = new ArrayList<E>();
 }

 public void push(E value) {
  if(value == null){
   throw new RuntimeException("Can't push null");
  }else{
   this.list.add(value);
  }
 }

 public E pop() {
  if(this.list.size() > 0){
   return this.list.remove(this.list.size()-1);
  }else{
   throw new RuntimeException("Nothing to pop");
  }
 }

 public E peek() {
  if(this.list.size() > 0){
   return this.list.get(this.list.size()-1);
  }else{
   return null;
  }
 }
}

At this point, the StackBehavior class runs seven behaviors that ensure the Stack class works according to Linda's (and a bit of my own) specification. The Stack class could probably use some refactoring (maybe the pop() method should call peek() as a test, rather than the size() check?), but thanks to my behavior-driven process so far, I have the infrastructure to make the changes in near total confidence. If I break something, I'll be quickly notified.

In conclusion

What you may have noticed about this month's exploration in behavior-driven development, or BDD, is that Linda was, in essence, the customer. You might think of Frank as the developer in this scenario. Take away the domain (in this case, data structures) and replace it with something else (say, a call center application) and the exercise is similar. Linda, the customer or domain expert, says what the system, feature, or application should do and someone like Frank uses BDD to ensure he has heard her correctly and implement her requirements.

For many developers, the shift from test-driven development to BDD is a smart move. With BDD, you don't have to think about tests, you can just pay attention to the requirements of your application and ensure that the application behavior does what it should to meet those requirements.

In this case, using BDD and JBehave made it easy for me to implement a working stack based on Linda's specifications. I just listened to what she was saying and then built the stack accordingly, by thinking in terms of behavior first. In the process, I also managed to discover a few things Linda had forgotten about stacks.


Downloadable resources


Related topics

  • "Behavior-driven testing with RSpec" (Bruce Tate, developerWorks, August 2007): One of the most promising innovations in developer testing in the past year is the introduction and rapid growth of RSpec, a behavior-driven testing tool. Learn how RSpec can change the way you think about testing.
  • "Introducing BDD" (Dan North, DanNorth.net, September 2006): Read how Dan North came up with BDD as a practice.
  • "Using BDD to drive development" (Andrew Glover, testearly.com, July 2007): Andrew offers another chance to see how BDD drives development, also based on using JBehave.
  • Download JBehave: A BDD framework for the Java platform that started it all.
  • In pursuit of code quality series (Andrew Glover, developerWorks): Learn more about writing quality-focused code.

Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java development, Web development
ArticleID=254866
ArticleTitle=In pursuit of code quality: Adventures in behavior-driven development
publish-date=09182007