Behavior driven development (BDD) is an evolutionary result of test driven development (TDD) in the sense that rather than thinking in terms of tests (which have the tendency to make you think after the fact) you can more easily think in terms of a specification. By thinking about an application’s specification or behavior, it becomes easier to validate things early– in fact, when thinking in terms of a specification, it becomes quite easy to write things upfront.
To take a simple example, imagine a customer has asked that you create some sort of validation service for the ordering flow of their online business. Specifically, customers keep mistyping zip codes, which unfortunately delays the shipping process (which happens to cause customers to complain and use up valuable customer service representatives’ time). Consequently, the customer believes this small improvement will save the company money. It is a simple job that won’t take long (but the consequences for messing it up are rather large).
Wanting to be agile, I want to get something quickly in front of the customer– but rather than jumping into the coding process, I’ll begin to specify behavior à la BDD (in this case, JBehave). At a minimum, it is decided that I’ll need an interface, which provides a simple contract:
public interface Validatable {
boolean validate(String value);
}
For a given String instance, an implementing class will return true or false based upon the validation requirements.
Next, I can specify a quick and dirty specification class (which I can show to my customer, if they speak code) that outlines a few, albeit sunny-day scenario, methods:
public class ZipCodeValidationBehavior extends UsingMiniMock{
public void shouldAcceptInvalidZipCode(){}
public void shouldDenyInvalidZipCode(){}
}
Note how this class doesn’t use the term ‘test’ — rather, the phrase ’should’ is utilized, because it turns out to be a bit more natural. Should equates to behavior, which is what I’m focused on at the moment.
As I haven’t actually coded a the business class yet, I’ll use JBehave’s mocking library to roughly specify my intended behavior. For example, given what I believe is a good zip code, the validation service should return true and consequently, given a bad zip code, the service should reject it.
To mock things easily, I create a simple helper method that returns a mock instance of the Validatable interface:
private Validatable getValidatable(boolean returnValue) {
Mock validator = this.mock(Validatable.class);
validator.expects("validate").with(new Matcher() {
public boolean matches(Object arg) {
return arg instanceof String;
};
}).atLeastOnce().will(returnValue(returnValue));
return (Validatable) validator;
}
This method creates a Mock instance and specifies that the validate method will return what ever value was requested at instantiation time (either true or false, in this case). In addition, the method specifies that the mock’s validate method may be called with a String as an argument (there is probably an easier way to specify this without having to create a new Matcher type, but I’ve yet to figure that out) and may be called more than once.
Keep in mind, the point of this mocking exercise is to help me flesh out my desired behavior– once things are kosher, I can presumably remove the mock and use the real object (which will immediately be verified with the behavior methods I’m about to implement).
I can now implement the shouldAcceptInvalidZipCode method like so:
public void shouldAcceptInvalidZipCode() {
List<String> goodzips = Arrays.asList("22101", "22101-5100");
Validatable validator = this.getValidatable(true);
for (String zip : goodzips) {
boolean value = validator.validate(zip);
ensureThat(value, eq(true));
}
}
As you can see, a List of what I believe are valid zip codes is looped over and the mock is utilized as expected– true is returned every time.
I can also implement the shouldDenyInvalidZipCode method as follows:
public void shouldDenyInvalidZipCode() {
List<String> badzips = Arrays.asList("221o1", "22101-100");
Validatable validator = this.getValidatable(false);
for (String zip : badzips) {
boolean value = validator.validate(zip);
ensureThat(value, eq(false));
}
}
Thus far, I haven’t written a line of real business code, but I’ve now established the behavior I intend to write. I can run this behavior via JBehave’s runner and see the veritable green bar:
..
Time: 0.046s
Total: 2. Success!
What’s more, I can even go over this behavior with my customer or even with the QA department, business analysts, etc– it doesn’t really matter and the intent is the same– does this behavior work according to your specification (in this case, your specification of a valid or invalid zip code?).
Assuming everyone in onboard, I can move forward, or if things are not correct according to a stake holder, I can make fixes and I haven’t really lost much– a few cycles in JBehave, but I haven’t yet written any real code.
If things are good to go forward, I can implement the Validatable interface with a real class that hopefully validates zip codes:
public class ZipCodeValidator implements Validatable{
private String zipRegEx = "^\d{5}([\-]\d{4})?$";
private Pattern pattern;
public ZipCodeValidator() throws Exception {
this.pattern = Pattern.compile(this.zipRegEx);
}
public boolean validate(String value) {
return this.pattern.matcher(value).matches();
}
}
With my class implemented, I can now refactor my behavior class and plug in the real deal. Once I’ve done that, I can rerun the behavior methods to verify things work as intended. First, I’ll refactor the getValidatable method like so:
private Validatable getValidatable(){
return new ZipCodeValidator();
}
Then, I need to fix each behavior method to invoke the proper getValidatable method; once that’s finished, I need to rerun the behavior class and voilà, things work as planned!
..
Time: 0.024s
Total: 2. Success!
As you hopefully can see through this example, BDD is an efficient way to drive the development of features in a rapid manner by communicating behavior rather than tests, which as I’ve found is a rather simple shift in thinking that opens the door to a wider audience of acceptance. Don’t get me wrong– I absolutely love writing tests in JUnit and TestNG, but I find these frameworks are a bit hard to get people to adopt upfront. No doubt, these frameworks offer a huge benefit when it comes to larger scale testing, such as component and system testing where large swaths of code are being verified.