JSpec: BDD for JS

A couple of weeks ago I wrote a nice little piece of JS that implemented BDD-style testing in JS. It was originally 70 or so lines of code, and supported code like:

jspec.describe("Some stuff", function() {
  it("should rock", function() {
    7.should("==", 7);
  });
});

It was (and still is) a very simple piece of code, that only dumps the output to the Firebug console. Because should() was a simple method, I was able to delegate out to a pluggable jspec.matchers object, which is where I specified the "match" definition, and allowed the customization of a "failure message." The original jspec had just two matchers ("==" and "include"), and allowed the creation of new ones by extending the jspec.matchers object (all JS, no magic).

When I tried to actually use it to test real-world code, though, some issues came up. First, functions were being dumped in full into the output, which was sucky. Secondly, the default describe syntax was sucking a bit for certain cases. And finally, I wanted you to be able to plug in any logger you wanted (for say, pretty HTML output). I added those new features, and am now releasing this quick and dirty code for people to look at.

Some examples

(which are included in the downloadable file)

``` jspec.describe("JSpec", function() { it("should support ==", function() { (1).should("==", 1); var arr = []; arr.should("==", arr); var obj = new Object; obj.should("==", obj); document.should("==", document); });

it("should support include", function() {
[1,2,3,4,5].should("include", 3);
[1,2,3,4,5].should_not("include", 3);
document.getElementsByTagName("div").should("include", document.getElementById("hello"))
});

it("should support exists", function() {
document.should("exist");
});

jspec.matchers["have_tag_name"] = {
describe: function(target, not) {
return jspec.compress_lines(this) + " should " + (not ? "not " : "") + "have " + target + " as its tag name."
},
matches: function(target) {
return (this.tagName && this.tagName == target) ? true : false;
},
failure_message: function(target, not) {
return "Expected " + this.toString() + (not ? " not " : " ") + "to have " + target + " as its tag name," +
" but was " + this.tagName;
}
};

it("should support custom matchers", function() {
document.getElementById("wrapper").should("have_tag_name", "DIV");
document.getElementById("wrapper").should_not("have_tag_name", "SPAN");
document.getElementById("wrapper").should("have_tag_name", "SPAN");
});
});


<h2>And the Output</h2>
<h3>In Firebug</h3>

JSpec


<h2>Some caveats</h2>
<ul>
  <li>Despite some work put in, I can't figure out how to get jspec to recover gracefully from errors and continue on. This will work in a future release</li>
  <li>I temporarily override the Object prototype (evil, I know) for the duration of the test. You will still be able to load in libraries that expect Object to work correctly, but you will not be able to test functions (such as prototype's Object.extend) without accounting for the fact that Object.prototype has been extended.</li>
</ul>

<a href="http://www.yehudakatz.com/wp-content/uploads/jspec.zip">Happy hunting (Download here)</a>

<p><b>Update</b></p>
<p>There is now a git repo for the project at git.caboo.se/jspec.git</p>