Test-driven development has evolved significantly over the last five years, and it seems ColdFusion hasn’t caught up. Automated testing has remained a painful process in ColdFusion. But it doesn’t need to stay that way—that’s why we’ve started working on cfSpec, an open-source project to support behavior-driven development in ColdFusion.
Behaviour-Driven Development
Like several other recent movements in software development, behaviour-driven development (BDD) is about clearly communicating the intention behind code. In practice, it is not very useful to think in terms of tests at all, but rather in terms of behaviours. A behaviour communicates intent, while a test simply asserts something. A behaviour is concerned with what the code should do, whereas traditional test-driven development often leaves us talking about the nouns in the code and using method names like testLineItem which don’t actually say anything about what the code being tested should do. For a more thorough introduction to BDD, read Dan North’s introduction.
Introducing cfSpec
In order to support this shift toward clarity of intent, we need new tools that automatically reinforce the new paradigm. These tools are already being used in several other programming languages, so we thought it was high time for such a tool in ColdFusion. cfSpec is being modeled after rSpec by David Chelimsky. It has begun as a basic unit-testing spec framework, and will eventually include stubbing/mocking and a full story framework. You can follow our progress at http://github.com/adelphus/cfspec/, or even pitch-in if you are familiar with these tools and concepts.
What Does It Look Like?
Full documentation will come as we approach our first stable release. In the meantime, here’s a very brief example of what a spec looks like in ColdFusion:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<cfimport taglib="/cfspec" prefix=""> <describe hint="User"> <before> <cfset user = $(createObject("component", "User"))> </before> <it should="be in any roles assigned to it"> <cfset user.assignRole("assigned role")> <cfset user.shouldBeInRole("assigned role")> </it> <it should="not be in any roles not assigned to it"> <cfset user.shouldNotBeInRole("unassigned role")> </it> </describe> |
And here is an unrelated example of what the current output looks like:


Joel
01/02/09 04:20 PM
John Farrar
01/02/09 06:48 PM
Sean Corfield
01/02/09 07:16 PM
Ron
01/02/09 07:59 PM
Adam Haskell
01/02/09 11:19 PM
srelliott
01/11/09 09:49 AM
Please test the behavior of the link to Dan North’s Intro to BDD ~ thanks!
Ron
01/11/09 10:38 AM
srelliott, the link is fine. It appears that Dan North’s site is having trouble since all pages except the homepage are giving that error. Here’s a link to Google’s cache of the article. which may work for a while.
Kevin Roche
01/25/09 05:23 AM
Ron,
I am very interested by cfSpec and BDD. I have struggled in the past with TDD and I think you have expained some of the difficulty. I have been deleyed a bit by Dan North’ site being down and now it is back up I have time to read it.
I have not has a chance to use cfSpec yet and maybe I missed somthing but in your examples you are using shouldNotBeInRole() and looks to me like there is a ShouldNotBe as we as a ShouldBe Matcher. Is there always a shouldNot for every matcher?
Ron
01/25/09 10:01 AM
Kevin,
You’ve made an excellent observation. Yes, all of the cfSpec matchers support both
shouldandshouldNotvariants. Although, in some cases, it’s not advisable to use the negative because it’s confusing (e.g.,shouldNotHaveAtLeast(5).children()makes much more sense rendered asshouldHaveAtMost(4).children().)