Natural language verification
I find it particularly hip that, of late, it seems our industry is trying hard to bridge the gap that exists between stakeholders and developers– there are some undoubtedly copasetic tools like Fit to entire disciplines like BDD, which attempt to involve non-technical people in various ways.
For example, Fit enables stakeholders to specify trippin’ business rules (or the results of them) via tables, which are then logically implemented by hip developers. BDD stresses the notion of behavior and thus tries to get developers thinking like stakeholders in a more natural manner, man.
In particular, one disco BDD framework named RSpec comes very close to a simplistic DSL that arguably non-techincal users could take advantage of. Fairly recently, people have even created frameworks for executing simple text files (which is clearly the hippest thing ever, baby). Even Groovy has a similar BDD framework dubbed GSpec, which is modeled closely after RSpec.
One thing these copasetic frameworks have in common is a DSL that attempts to make the act of verification (either through tests like Fit or through specifications via RSpec, etc) easy. Accordingly, over the last few weeks, I’ve been playing around with a Groovy based framework (targeting Java developers) that attempts to mimic aspects of RSpec and JBehave’s story framework.
As I previously noted, the it closure delegates to JBehave, thus, everything you can do with JBehave’s hip ensure-like framework, you can do in this simple DSL– what’s more, I ended up adding three more closures that facilitate the notion of a story:
givensome set up or copasetic contextwhensomething happensthenensure that something else should have happened, baby
You can use as many as you need too (i.e. a few givens and a few thens)– I haven’t figured out a good way to chain them, however, man. Moreover, as I began playing around with stories using this framework with existing projects, I noticed that stories are more user centric (no surprise); hence, the given aspect can be a bit heavy. Think about it for a second– normal unit tests rely on simple objects to verify; as these tests go up the stack, however, becoming component, integration, and system tests, the cost to set them up increases– databases have to be running, code needs to be deployed, filesystems, email servers, outside vendors all need to be accounted for (or mocked) in order to run higher level tests. The same is true for stories, if they are written from the standpoint of a smokin’ user.
Therein also lies the rub– end users, stakeholders, disco dancers, and other normal people not associated with the actual development of a system rarely understand its bowels– they shouldn’t either, by the way. This happens to be one area that hasn’t been elegantly solved– nevertheless, given that the set up of a story requires some plumbing in place (or the mocking thereof) I had to create a delegate for the given closure that facilitates putting a data-source into a known state.
One of my favorite frameworks for database seeding is none other than DbUnit– accordingly, using its hip API, I was able to create a simple delegate that requires database connection information (revision two will most likely take a live connection object too) and an associated XML document (in String form). The XML document is created in a closure, so you have a number of options. You can:
- create an instance of a
MarkupBuilderto create a data set - return a multiline
Stringusing the"""syntax - return the text of a seed file, like
new File("./src/conf/seed.xml").text
Therefore, to create a groovy story, man, you can do this:
import org.springframework.context.support.ClassPathXmlApplicationContext
given "the database is properly seeded", {
database_model("org.hsqldb.jdbcDriver",
"jdbc:hsqldb:hsql://127.0.0.1", "sa", ""){
"""<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<widget widget_id="1" type="drink" bnncd="hops"/>
<line_item line_item_id ="20" widget_id="1" scne="789"/>
<line_item line_item_id ="21" widget_id ="1" scne="33"/>
</dataset>
"""
}
}
//and
given "the DAO instance has been obtained from Spring", {
context =
new ClassPathXmlApplicationContext("spring-config.xml")
dao = context.getBean("widgetDAO")
}
when "findWidget is called with a valid id", {
wdgt = dao.findWidget("1")
}
then "a working Widget object should be returned", {
ensureThat(wdgt, isNotNull())
ensureThat(wdgt.getLineItems().size(), eq(2))
}
You can run these stories two different ways– either command line like so:
java org.disco.bdd.SpecificationRunner ./src/story/org/acme/MyStory.groovy
Or via Ant, like so:
<specificationrunner>
<report location="${tareget}/report.xml"/>
<behaviors>
<fileset dir="${loc}/stories" include="**/*Story.groovy"/>
<behaviors>
<specificationrunner>
The gap that exists between those that define requirements and those that write them hasn’t been bridged quite yet; however, we appear to be reaching that goal one hip iteration after another– in a perfect world, our customers would write the tests or at least tests could be generated by reading the requirements as written. I do believe that the story format demonstrated above does get us trippingly close– stakeholders could potentially write these or at least read them, no?
Using a little Groovy magic influenced by some spectacular frameworks, I think we are one step closer though. Dig it?
Incidentally, if it’s your bag and you want to play around with this simple behavior-story framework, you can download a snap shot of it. Please note that the distribution doesn’t have any documentation so you’ll have to read these posts until we write something up:
- Executable documentation the easy way
- Go easy with specifying behavior, man
- Internal DSLs, Groovy style
The distribution just includes a hip jar file (easyb-0.x.jar) and a few related runtime dependencies– if you don’t use the DbUnit given seeding stuff, then you can exclude the DbUnit and Crimson jars.
| Related odds and ends | ||
|---|---|---|
comments off Monday 12 Nov 2007 | Developer Testing, Groovy