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).
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 likepush()andpop(). Sometimes you'll see apeek()method, as well.
Frank: What doespush()do?
Linda:push()takes an input object, sayfoo, and places it into an internal container (like an array).push()usually doesn't return anything either.
Frank: What if Ipush()two things, likefooand thenbar?
Linda: The second object,bar, should be on top of the conceptual stack (containing at least two objects), so that if you callpop(),barshould come off instead of the first object, which, in your case, isfoo. If you calledpop()again, thenfooshould be returned and the stack should be empty (assuming there wasn't anything in it before you added the two objects).
Frank: Sopopremoves 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 callpop()without having pushed anything?
Linda:pop()should throw an exception indicating that nothing has been pushed yet.
Frank: What if Ipush()null?
Linda: The stack should throw an exception becausenullisn't a valid value topush().
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.
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 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. You may
recall a similar pattern of overriding a convenience class in my previous
article about unit testing
Ajax with the Google Web Toolkit; in that case, the override was done with
GWT's Timer class.
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!
|
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.
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"));
}
|
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");
}
}
|
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);
}
|
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.
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.
Learn
-
"In pursuit of code
quality: Unit testing Ajax applications" (Andrew Glover, developerWorks, July 2007):
Testing Ajax applications gets easier with GWT and its overriding
Timerclass on the job. -
"In pursuit of code
quality: Programmatic testing with Selenium and TestNG" (Andrew Glover,
developerWorks, April 2007): Learn how to run Selenium tests programmatically, using TestNG as the test driver.
-
"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.
-
"Mocks
are hip when it comes to BDD" (Andrew Glover, thediscoblog.com, July 2007): Andrew
re-discovers mock objects via JBehave's mocking library, which he then uses to drive rapid development.
-
In pursuit of code quality series (Andrew Glover, developerWorks): Learn more about writing quality-focused code.
-
developerWorks: Hundreds
of articles about every aspect of Java programming.
Get products and technologies
-
Download JBehave: A BDD framework for the Java platform that started it all.
Discuss
-
Discussion forum: Improve your code quality: Learn from the code
quality perfectionist! Andrew Glover shares his expertise as a consultant
focused on improving code quality.

Andrew Glover is president of Stelligent Incorporated, which helps companies address software quality with effective developer testing strategies and continuous integration techniques that enable teams to monitor code quality early and often. Check out Andy's blog for a list of his publications.



