Updates on Inspec - Javascript BDD Framework

Posted on April 24, 2009

For the past two weeks, a lot of improvements has been done for Inspec, here is the list:

  • Better scoping / sandboxing
  • Support for Rhino, SpiderMonkey, Johnson and WSCript
  • Tested to work with IE7, Firefox 3, Chrome and Safari 3
  • Added a lot more specs to test Inspec
  • Fixed a bug where cascading before / after blocks not getting the correct scoping / sandboxing
  • Changed BDD syntax a bit to be compatible with Screw.Unit
  • Shamelessly stole all matchers from Screw.Unit
  • Added the following matchers:
    • beA – instanceof test
    • throwError – takes an function, and see if it throws an error
    • respondTo – test if an object has a function
    • have – see if an object or array contains something

I’m working on some advanced features that requires some pretty big structural changes right now. They are:

  • Support for multiple definition of same behaviors.
  • Support for multiple definition of before / after blocks.
  • Redo shared example groups ( Allows for sharing local scope variables inside shared example groups)

I plan to finish this feature while I’m taking a business trip to Boston this coming Sunday.

For future, the plan follows:

  • Spec, spec spec, 100% coverage require!
  • Extract and improve rendering logic from HTMLReporter to HTMLReporter.FlatFormatter
  • Implement HTMLReporter.NestedFormater
  • Implement HTMLReporter.CompactFormater
  • Implement ConsoleReporter.FlatFormatter
  • Implement ConsoleReporter.NestedFormater
  • Implement ConsoleReporter.CompactFormater
  • Implement options for choose formatters, need to consider extending
  • Add statistics functionality to Reporter Class
  • Test on different browsers
  • Selective Behavior Execution support
  • Prioritize object printing with toString() if object has customized toString
  • Documentation
  • Make a wiki

That’s a lot of stuff to complete! If anyone interested please fork Inspec

Inspec - Yet Another Javascript BDD Test Framework

Posted on April 13, 2009

The “Why” Question

Why? You ask, we already have BDD Frameworks such as Screw.Unit, jSpec, and JSSepc, you want to make another Javascript BDD Test Framework?

To answer your question, you can take a look at my previous article comparing strengthes and weaknesses of the above frameworks. So, please read it if you haven’t done so.

Now, if you are still with me after finish reading my article, allow me to introduce you a new BDD Test Framework that doesn’t suck.

Introducing Inspec

While coding up Inspec, I was trying to fulfill the following features:

  • Nested behaviors
  • Shared behaviors (aka. it_should_behave_like in RSepc)
  • Framework agnostic ( You can use it with jQuery, Prototype, Mootools, YUI, or ExtJS. it’s your choice. )
  • An elegent DSL
  • No namespace pollution
  • Able to run in browser, command-line, and server side
  • A cystal clear API that is easy to understand and extend

Sound awesome! But how does it work?

Here is a small example:

describe("Inspec", function(){

  it("should work", function(){
    expect(true).toBeTrue();
  })

  it("should fail", function(){
    expect(true).not().toBeTrue();
  })

  it("should be pending")

  describe("with a nested example group", function(){    
    it("should work as a nested example group", function(){
      expect(false).toBe(false);
    })
  })

  // W00t?? It's nesting a shared example group
  itShouldBehaveLike("a shared example group");
})

sharedExamplesFor("a shared example group", function(){
  it("should work as shared example", function(){
    expect(true).toBeTrue();
  })

  // Yes! Shared nested Example Groups!
  describe("with nested example groups in shared", function(){
    it("should work as a nested example group in shared", function(){
      expect(true).toBeTrue();
    })
  })
})

Here is another example that uses more matchers:

describe("Matchers", function(){
  describe("TypeMatcher", function(){
    it("should work with String", function(){
      var aString = "abc";
      expect(aString).toBeType("string");
    })
    
    it("should work with Number", function(){
      var aNumber = 123;
      expect(aNumber).toBeType("number");
    })
    
    it("should work with Object", function(){
      var anObject = {};
      expect(anObject).toBeType("object");
    })
 
    it("should work with Boolean", function(){
      var aBoolean = false;
      expect(aBoolean).toBeType("boolean");
    })
    
    it("should work with undefined", function(){
      var undefinedValue;
      expect(undefinedValue).toBeType("undefined");
    })
 
    it("should work with function", function(){
      var aFunction = function(){};
      expect(aFunction).toBeType("function");
    })
  })
  
  describe("InstanceMatcher", function(){
    it("should work with String", function(){
      var aString = new String("abc");
      expect(aString).toBeA(String);
    })
    
    it("should work with Number", function(){
      var aNumber = new Number(123);
      expect(aNumber).toBeA(Number);
    })
    
    it("should work with a class", function(){
      var Foo = function(){};
      var foo = new Foo();
      expect(foo).toBeA(Foo);
    })
 
    it("should work with a sub-class", function(){
      var Foo = Inspec.Class.extend({});
      var foo = new Foo();
      expect(foo).toBeA(Foo);
      expect(foo).toBeA(Inspec.Class);
      expect(foo).toBeA(Object);
    })
  })
  
  describe("ComparisonMatcher", function(){
    it("should work with toBeAtLeast", function(){
      expect(5).toBeAtLeast(5);
      expect(6).toBeAtLeast(5);
    })
    
    it("should work with toBeAtMost", function(){
      expect(4).toBeAtMost(5);
      expect(5).toBeAtMost(5);
    })
    
    it("should work with toBeGreaterThan", function(){
      expect(6).toBeGreaterThan(5);
      expect(5).not().toBeGreaterThan(5);
    })
    
    it("should work with toBeLessThan", function(){
      expect(4).toBeLessThan(5);
      expect(5).not().toBeLessThan(5);
    })
  })
  
  describe("RegexMatcher", function(){
    it("should match correct string", function(){
      var emailRegex = /^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$/;
      var email = "abc@efg.com";
      expect(email).toMatch(emailRegex);
    })
    
    it("should not match incorrect string", function(){
      var emailRegex = /^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$/;
      var email = "abc_efg.com";
      expect(email).not().toMatch(emailRegex);
    })
  })
})

What’s working so far

As this is still an early release (Let’s see, It is about 3 weeks old so far. Let’s call it version 0.0.1??), most of the core features are already working. Here is the list:

  • Basic Matchers
  • Nested describes
  • Shared describes
  • Sandboxed Example Scope
  • Tested to work in FireFox
  • Basic HTML Reporter
  • Works with Rhino and Johnson
  • Basic Console Reporter for Rhino and Johnson

In The Near Future…

While Inspec is now working with basic matchers, here are a few things I’d like to add:

  • Better syntax and organization of matchers for easier extensions. (Tempted to steal from Screw.Unit)
  • More Matchers. (Makes everyone happy.)
  • Work on WScript environment.
  • Make sure compatibility with all major browsers. (Helps appricated)
  • Allow selective execution of Example groups / Examples.
  • Make a better HTML Reporter, that hooks into selective executions.
  • Better Documentations. (Helps appricated)
  • Better Error messages on Failure and Exception.
  • Package and maybe minify(why not?).
  • A LOT more rigid and concrete tests to reach 100% coverage. (Also serve as uage examples, helps appricated)

For Developers

I have been very busy with my date job lately, and never have enough time to work on my own projects. Any help is very much appricated. So if you are interested in developing Inspec together, just fork me, make some improvements, rebase, push, and shoot me a pull request. If you are a regular contributor, your name will be in the co-author’s list as well.

Suggestions? Comments?

While I primarily developed this for my own need. I hope this will benefit everyone who has similiar needs. So your suggestions and critics are very welcome. Tell me what you like about it, what you don’t like about it, and how Inspec can be improved. Sorry that there is nothing like google group or irc or mailing list setup yet. You can leave a comment here, and I will be notified automatically through email. If this project grows bigger, I will setup a google group and an irc channel. But for now, you can either leave a comment, or scroll down to the bottom of the page, and my contact information is on the lower right.

Javascript BDD Test Frameworks Compared

Posted on April 12, 2009

Lately, I’ve been trying to find a good JavaScript BDD style test framework, and I have come across a few of them such as Screw.Unit, jSpec, and JSSepc. They all have their own unique set of features, and are all very innovative. But I couldn’t find a exact fit for my requirements. While they are all unique in their own ways, and provide some rather innovative approach to the BDD problem domain, they all have some drawbacks that I find either not acceptable or not easy to use.

Let’s have a little analysis on the strength and weakness of each frameworks listed above. Ok, you must note that the strength and weakness I describe is obviously a subjective opinion of my own, that entirely pertains to my own need. You may find the weakness I mentioned to be a strength in your case. Don’t flame me about it!

Screw.Unit

Screw.Unit is the one I found to be the closest to my needs, and I believe it is the most popular BDD JavaScript test framework at the momenent of this writing.

Things I like about Screw.Unit

A key feature of Screw.Unit is the support for nested describes and the cascading before (and after) behavior. For presentation, it has a good HTML runner that generates good looking HTML pages to present the test results. You can also create custom matchers in a easy and declaritive manner to extend Screw.Unit. Because adding member functions to Object is consider a bad practice, the use of foo.should be_true cannot be done easily without polluting the Object prototype. However they came up with an alternative DSL that looks like this: expect(foo).to(equal, true)

Things I don’t like about Screw.Unit

However, there is a few things I don’t like about it.

  1. No support for shared specs. (aka it_should_behave_like in RSpec)
  2. Does not support commandline (due to dependcy on jQuery, DOM, and concrete Javascript)
  3. Does not support server-side javascript (Same reason above)

jSpec

jSpec is probably the most innovative JavaScript BDD, and sometimes I feel that it might be a bit too innovative…

Things I like about jSpec

In jSpec, you don’t write true JavaScript, but a higher level DSL, which later being parsed and converted to JavaScript and executed. It also supports nested describes and before, after blocks. It has a wealth of matchers, and has formatters that supports DOM, console, and terminal. The size of the script is very small as well.

Things I don’t like about jSpec

First, doesn’t support shared behaviors.

The DSL looks nice at first sight. In fact I almost mistook it as rSpec test suits. But after careful analysis, I have a few doubts about jSpec’s DSL. First, from the source code, the DSL to JavaScript transformation is done using a few lines of Regex only. I’m afraid that it might not work on edge cases. Next, I looked at the tests for the Regex transformation, and I found that the tests are not very strict either. Also, I think this makes error tracking rather difficult, since the DSL is transformed, it is hard to figure out where the error is if you run into some edge cases. I’d rather write JavaScript and be at ease, instead of write poorly supported FrankensteinScript, and have no clue where went wrong.

Another part I don’t like about jSpec is the source code is rather cryptic. I have to say the author of jSpec is a very smart person. To me, his code is 50% recursive functional rocket science and 50% regex dark magic. It took me a long time to decrypt how the code is working under the hood, and I can’t say I understood it 100%. This makes contributing and extending very difficult for most busy programmers (including me) that just want to get the things done.

JSSpec

JSSepc is probably the oldest Framework that started to tackle the BDD domain in JavaScript. The initial release is dated July 16, 2007. Its features are relatively simple compare to above two frameworks.

Things I like about jSpec

JSSepc’s source code is simple and clean, and relatively easy to extend. It has very original syntax as well, Instead of using expect(foo).to(equal, true), its format is value_of(foo).should().be_true(), a bit less sweet… However, it is the first (I think) to tackle BDD in JavaScript, it is nice that they first invented this approach, and paved way for newer frameworks to learn from. The HTML report is very nice too. You can selectively run a set of suit right in the browser.

Things I don’t like about jSpec

  • It doesn’t support nested describe blocks.
  • It doesn’t support shared behaviors.
  • Not in active development. (Last commit was Dec. 2008)