Skip to main content

BDD with ease with Easyb

Posted by johnsmart on July 22, 2008 at 7:49 AM PDT

Behaviour-Driven Development, or BDD, is considered by many to be a natural extention of Test-Driven Development (or TDD). Test-Driven Development is about designing software with the tests in mind.This tends to make the detailed design of your classes cleaner, more modular and more flexible. In practice, it involves writing your tests at the same time as your code - before, simultaneously or slightly after. As a bonus, if you do it well, you get a high degree of test coverage and a good set of regression tests into the bargin.

But one of the biggest problems that people come up against is knowing what tests to write. It's very easy to write a set of superficial unit tests that execute virtually all of your code but actually test very little in semantic terms. Andy Glover covers this issue well in his article on Test Coverage.

BDD proposes an interesting solution to this problem - instead of naming your tests "testThis" or "testThat", you call them "shouldDoThis" and "shouldDoThat". Immediately, you are thinking about what your class "should" do - in other words, you are thinking specifications. The difference is subtle, but this turns out to be a much more intuitive way of driving development than thinking in terms of raw tests. Rather than saying "what do I test", you are saying "what should my class actually do", or "how should my class behave". Which is very much the spirit behind TDD.

Enter Easyb. Easyb is a BDD framework for Java. Basically, you describe your class's intended behaviour in a simple domain-specific language, using terms like "given", "when" and "then". The test cases are readable, and serve both as design documentation and as unit tests. Suppose for example you want to test your new bank account class. You need to ensure that, if you overdraw, the code throws an exception and that the account balance remains unchanged (we haven't implemented bank fees yet ;-) ). A simple Easyb test case to do this might look like this:

      given "a bank account containing $100", {
        bankAccount = new BankAccount(100)
      }
      when "$150 is withdrawn", {
overdraw = {
bankAccount.withdraw(150)
        }
  }
  then "an InsufficientFundsException exception should be thrown", {
         ensureThrows(InsufficientFundsException.class){
             overdraw()
         }
      }
      and "then the account should still contain $100", {
          bankAccount.balance().shouldEqual 100
      }

EasyB uses a very readable Domain Specific Language (DSL), that you use to write very readable (pretty much self-documenting, in fact) test cases. Rather than reasoning in terms of asserts, or even assertThats, you describe your test case in terms of simple structured user stories. Instead of assert statements, you have expressions such as "shouldEqual", "shouldBeLessThan" and "shouldHave" (for collections), which have the same natural feel as Hamcrest expressions, and are just as readable. The code itself is in Groovy, which is close enough to Java to be very easy to pick up even if you have no prior experience with Groovy scripting.

So, if you're interested in Behavior-Driven Development, go check out the EasyB website to learn the groovy moves and disco danse steps of BDD with EasyB in the inimitable style of my good friend Andy Glover.

Comments

You can find a sample Maven project using Easyb at http://www.wakaleo.com/resources/easyb-code-samples

Can you, please, attach the sample maven project with this code. I'm trying to create my own using mvn archetype:create and pasting this info to the pom.xml, but when I use mvn clean test it fails with the next message: [INFO] Using easyb dependency org.codehaus.groovy:groovy-all-minimal:jar:1.5.0:compile [java] Exception in thread "main" java.lang.NoSuchMethodError: org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeStaticMethodN(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object; [java] at org.disco.easyb.report.TxtStoryReportWriter.(TxtStoryReportWriter.groovy) [java] at org.disco.easyb.ConsoleConfigurator.getConfiguredReports(ConsoleConfigurator.java:63) [java] at org.disco.easyb.ConsoleConfigurator.configure(ConsoleConfigurator.java:30) [java] at org.disco.easyb.BehaviorRunner.main(BehaviorRunner.java:52) [java] Java Result: 1