Executable Documentation - EasyB style

Posted by Ken Brooks Mon, 04 Feb 2008 21:45:00 GMT

One of the challenges of maintaining and enhancing an existing system is that the documentation goes out of date probably before you even finish it the first time. Whenever someone has a question for me about how does something work I usually refer to the location of truth. That would be the code itself, which is always the most up to date source of documentation. So the closer to the source you get the better chance your documentation has of standing the test of time.

Unit Tests helps in this area in that they can outline the assertions we are placing on our code. That too must stay up to date because it is also close to the code. Unfortunately unit tests are often more complex than the code they are testing and ultimately end up not being very good sources of documentation either.

There has to be a way to build human (possibly even business person) consumable documentation that stays in sync with the truth.

Enter EasyB.

EasyB is a BDD testing framework written in groovy and java. It provides what we like to call executable documentation. This is achieved by taking TDD, all sopping wet with techy mess, and throwing it in the evolutionary dryer with a nice fabric softener. Out from the other end pops a nice fluffy testing framework that can not only provide up-to date documentation about the application every time it is built, but can even bridge the gap between business requirements and developer interpretation.

It has two supported ways to write specifications. Behaviors and Stories. Its just a matter of personal taste on which you use for each component in your project. Behaviors often are shorter and good for utilities. Stories are more verbose and usually map to a series of interactions.

Behaviors look like this:

it "should return null for null input to trim", {
  ensure(StringUtilPartial.trim(null)) {
    isNull
  }
}

it "should return the string without whitespace at either the end", {
  StringUtilPartial.trim(" somestring ").shouldNotBe null
  StringUtilPartial.trim(" somestring ").shouldBe "somestring"
}

Stories look like this:

scenario "appending string to empty string buffer", {
  given "an empty string buffer", {
    stringBuffer = new StringBuffer()
  }

  when "a string is appended", {
    stringBuffer.append("somestring")
  }

  then "the buffer should contain the string appended", {
    stringBuffer.toString().shouldBe "somestring"
  }
}

scenario "appending string to existing string buffer with existing data", {
  given "a string buffer with an initial value", {
    stringBuffer = new StringBuffer("abcd")
    originalStringBufferValue = stringBuffer.toString()
  }

  when "a string is appended", {
    stringBuffer.append("somestring")
  }

  then "the buffer should contain the original value plus the appended value", {
    stringBuffer.toString().shouldBe(originalStringBufferValue + "somestring")
  }
}

What you have just witnessed is that we have tested our code and written documentation at the same time. That documentation will evolve right along with the code its testing and nobody has to be the low man on the totem pole and write documentation.

All well and good, but what we wrote above still wouldn’t be what a business person would consider documentation. Too much techy goobledeegook in there. Easyb has another trick up its sleeve especially when dealing with the story based specifications. When I ran the story above a report is generated and here is what the contents look like:

  Story: string buffer
    scenario appending string to empty string buffer
      given an empty string buffer
      when a string is appended
      then the buffer should contain the string appended
    scenario appending string to existing string buffer with existing data
      given a string buffer with an initial value
      when a string is appended
      then the buffer should contain the original value plus the appended value

What?! Where did that english documentation come from? Shhh.. don’t tell your developers that they wrote it.

Check out easyb at easyb.org and join the group to keep up on the latest developments. The current release of easyb is 0.6 and some of the syntax presented above (shouldBe for example) is in the trunk and not a release yet.

Posted in  | Tags , ,

easyb plugable functionality

Posted by Ken Brooks Sun, 23 Dec 2007 20:36:00 GMT

if you aren’t familiar with easyb (http://easyb.org), then do yourself a favor and pay that site a visit to learn the basics of this great bdd framework for java.

one of the strengths of easyb is its domain specific language that allows us to write behaviors and stories that act as executable documentation. we also have a way to allow you as a user to extend that by writing plugins.

here are the steps to do so:

  • implement Plugable on our plugin classes (optional)
  • create the following file META-INF/services/org.disco.easyb.core.delegates.Plugable
  • package it all up into a jar and make it available on the classpath

lets start with our class, write any java/groovy class that exposes a public method and (for readability) implement the org.disco.easyb.core.delegates.Plugable interface.

now, create a file named org.disco.easyb.core.delegates.Plugable and when you package everything into the plugin jar put that file into the META-INF/services directory. the contents of the file need only be a list of classes (fully qualified) that optionally implement the Plugable interface and expose public functionality you wish to mixin to easyb.

package your classes and the property file into a jar file (outside the scope of this article) and make sure that jar file is available on the classpath when easyb stories are run

three steps, thats it? seem too simple! thats what easby is all about.

So whats happening under the covers? when easyb encounters a method call in the story that it can’t resolve it then goes thru the list of classes found in the META-INF/services/org.disco.easyb.core.delegates.Plugable file. Each class is then inspected to see if it contains a method that can handle our call. If it finds one then everything is golden and the call is made, if not then a MethodMissing exception is thrown.

If you wish to see an example of this completely implemented then checkout the easyb source and look in the plug-ins dir for easyb-dbunit. This mixes in dbunit functionality into easyb.

Tags  | no comments