RSpec‘s DSL is so hip that I recently found myself wanting that same simple expressiveness in Java (by the way, there’s nothing stopping someone from using RSpec with Java via JRuby)– in fact, what I want is a simple framework that facilitates letting non developers specify behavior. For instance, taking a terribly simple (yet hip) example, imagine (again) a queue data structure. If you were to break it down into a simple narrative, you may end up with something like this:
A queue’s basic behavior can be descirbed as:
it:
- should throw an exception if
nullisenqueued- should throw an exception if there isn’t anything to
dequeueanddequeueis called- should
dequeuefirst itemenqueued- should
dequeueitems in same order asenqueued
In a perfect world, I could run this file as is (also, in a perfect world, disco music would be the only thing heard on the radio, in elevators, in cafes, and at parties). Given that I’m close to having my wish with disco music, I figured I’d try my luck with running the file as well. Well, maybe not running the file as is, but trying to get close.
I started by defining a DSL (backed by a Groovy builder) that looked something like this:
def specification = new SpecificationBuilder();
specification.QueueBehavior{
before("each":new Queue()){}
"should dequeue first item enqueued"{
when(enqueue:5){
ensure(dequeue:51)
}
}
"should raise an exception if null is enqueued"{
when(enqueue:null){
ensure(Exception:RuntimeException.class)
}
}
"should dequeue items in reverse order when enqueue is called"{
when(enqueue:[5, 7, 9]){
ensure(dequeue:[5,7,9])
}
}
}
But, I quickly discovered this particular format was somewhat limiting given the behavior desired in the maps created in the when and ensure elements. For instance, the last behavior (the one labeled "should dequeue items in reverse order when enqueue is called" is ambiguous– what I’m trying to convey is multiple method invocations of enqueue, but it could also mean invoke the enqueue method with three parameters). The SpecificationBuilder class was also becoming quite complex as I had to deal with dynamic method invocation and a lot of parsing.
Nevertheless, the intent is there in the above code– note, the code doesn’t assume too much knowledge of the underlying object whose behavior is being specified– simple Strings are used instead of method invocations. The non developer pipe dream does break down a bit though. In my case, they’d have to know about RuntimeException exceptions, for example.
By the way, if you haven’t already done so, check out GSpec– this is a well done framework and I’m not here to rewrite it; however, its groovy author stated that GSpec is “still mostly concept”– so I figure that gives me some liberty to explore some of my concepts. In doing so, I have a deep appreciation for what he’s already accomplished (plus, Cliff is one hip dude, man).
Also, I wanted to make use of JBehave, which my first attempt above did do underneath the covers. Feeling somewhat frustrated with my efforts in trying to make something interesting, I turned to two incredibly smart guys (who know are also groovy about Groovy) at a recent conference: Venkat Subramaniam and Jeff Brown. These guys are superstars who can provide a lot of information quickly– I made sure to listen anytime one of them opened their mouth, baby.
Sitting down with these two copasetic cats on Saturday evening yielded something quite interesting. Rather than building a DSL via Groovy’s builders, we set off, under Venkat‘s advice, in creating an internal DSL using plain old Groovy. We modeled it after RSpec and used the venerable Queue example as a baseline. After some coding, here and there, we ended up with this:
import org.disco.bdd.Queue
queue = new Queue()
it "should dequeue gives item just enqueued", {
queue.enqueue(2)
ensureThat(queue.dequeue(), eq(2))
}
it "should throw an exception when null is enqueued", {
ensureThrows(RuntimeException.class){
queue.enqueue(null)
}
}
it "should dequeue items in same order enqueued", {
[1..5].each{ val ->
queue.enqueue(val)
}
[1..5].each{ val ->
ensureThat(queue.dequeue(), eq(val))
}
}
Notice that this Groovy script makes use of JBehave’s Ensure library (it even uses some newly created methods like ensureThrows, which obviates JBehave’s Block type). This means that everything you can do in JBehave is at your fingertips when defining these disco behaviors.
Running this script to keep on truckin’ is easy too. I purposely created a Java based runner so as to make this “idea” framework palpable to a wider audience. Accordingly, specification authors don’t really even need to know that the specification script is Groovy.
Running this script is as easy as typing:
ag$ java org.disco.bdd.SpecificationRunner ./src/groovy/org/disco/bdd/QueueSpecification.groovy
The first argument to the runner is the actual specification, man. This concept framework is by no means totally in the groove– for instance, reporting mechanisms are not present; however, the base is finished (thanks to both Jeff and Venkat). Incidentally, the plumbing of this framework is quite cool as it makes use of Closure delegates quite elegantly (many thanks to Jeff for this suggestion).
While I still can’t run a hip text file narrative as written above in Java, this specification DSL using Groovy and Java proved to be fun to write and, in my opinion, makes it easier to write a behavior than the more fuzz style Java-JBehave way. Can you dig it, man?

RSS Feed
Twitter
October 31st, 2007
Posted in