Javascript Test Driven Development With YUI Test

Tonight I’ve decided to bounce back and try out another testing framework that’s been on my list for awhile, YUI Test. From the site:

YUI Test is a testing framework for browser-based JavaScript solutions. Using YUI Test, you can easily add unit testing to your JavaScript solutions. While not a direct port from any specific xUnit framework, YUI Test does derive some characteristics from nUnit and JUnit.

Fair enough… lets look at the dirty details and then get started.

The Dirty Details

License
BSD License

Last Updated
I was unable to get any solid dates for the last date it was updated, but as it has gone through several revisions (via the changelog) I think we can assume it is actively developed.

Support
The documentation is strong. You can also seek help on the ydn-javascript mailing list.

Let’s Get Started

We can get it easy enough from the YUI configuration page and copy the required script tags needed. It’s pretty nice to not have to download all the dependencies… just include them from the CDN and you’re good to go. HOWEVER, if you want your tests to run without a net connection, you might want to download them. :)

Fair enough, let’s go ahead and write our first test. I noticed that assertions look something like YAHOO.util.Assert.areEqual, which is nicely namespaced but a bit too long for my blood, so the style I’ll be using for this example is to create the test case within a function so I can locally scope assert to YAHOO.util.Assert. With that said, here’s the first test case:

var placeholderTestCase = (function(){
  var assert = YAHOO.util.Assert;
  var PLACEHOLDER_TEXT = "Enter Your Name";

  var test = new YAHOO.tool.TestCase({
    name:"HTML5 Placeholder Emulator Test",
    setup:function(){
      var sandbox = $('<div><input type="text" id="foo" placeholder="'+PLACEHOLDER_TEXT+'"></div>');
      this.emulator = new Html5Emulator(sandbox);
      this.input = $('#foo', sandbox);
    },
    tearDown:function(){
      delete this.emulator;
      delete this.input;
    },
    testValueIsPopulatedWithPlaceholderAttribute:function(){
      this.emulator.emulatePlaceholders();
      
      assert.areEqual(PLACEHOLDER_TEXT, this.input.val());
    }
  });
  return test;
})();

Nice and clean, with a locally scoped constant and shorthand assert. I’m going to go ahead and put this in a standalone js file and write an html file as the runner (all.tests.html). The cool thing about YUITest is it has pretty much decoupled the running of tests from the display of test results. So we also need to create a test logger to report results.

<html>
  <head>
    <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.7.0/build/logger/assets/skins/sam/logger.css" />
    <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.7.0/build/fonts/fonts-min.css" />
    <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.7.0/build/yuitest/assets/skins/sam/yuitest.css" />
    <script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>

    <script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/logger/logger-min.js"></script>
    <script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/yuitest/yuitest-min.js"></script>

    
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
    <script type="text/javascript" src="lib/html5.emulator.js"></script>
    <script type="text/javascript" src="html5.placeholder.test.js"></script>
    <script type="text/javascript">
    YAHOO.util.Event.onDOMReady(function (){
        with(YAHOO.tool){
          var logger = new TestLogger("testLogger");
          TestRunner.add(placeholderTestCase); 
          TestRunner.run();       
        }     
     });
  
    </script>
  </head>
  <body class="yui-skin-sam">
    <div id="testLogger"></div>

  </body>
</html>

Now in this case, I used the javascript with statement to scope to YAHOO.tool so I don’t have to include the package name for each Object instance (it is considered harmful though). Now I switch over to firefox, open up the page and see…

This is just using the basic logger, which sadly doesn’t give us a red/green bar. However it does tell us it failed, which is really good enough for me for now. The cool thing is you can listen to different events and implement your own runner to report results however you want, and YUI provides a reporting tool that can post results to any url, so ideally you could set up some kind of service to capture and record results as part of your CI process. For example:

var oReporter = new YAHOO.tool.TestReporter("http://www.yourserver.com/path/to/target", YAHOO.tool.TestFormat.JSON); 

I really like it… it’s pretty nifty and very extendable. :)

I’ll spare you the boring details and continue test driving the ol’ HTML5 placeholder example until we get to see 5 passing tests:

As usual, the code is on github for your viewing pleasure.

What I Liked

  • Very very decoupled… various events are triggered for the various test states, and you can subscribe to these events for reporting purposes
  • Reporter that allows you to post results to a url in JSON and XML formats (possibly more, but these were the ones I saw).
  • Doesn’t pollute the global namespace, everything (as is everything in the YUI library) is namespaced
  • Ability to include special instructions (like ignore errors or ignore a specified test)
  • Capability to perform asynchronous tests
  • Can run test suites and provides suite level setUp/tearDown in addition to test level setUp/tearDown
  • EXCELLENT documentation

What I Didn’t Like

I didn’t really find much to not like about it, it’s a pretty solid testing framework. If I had to nitpick, I’d probably say I like the ability to just open a test case and see it run in some frameworks… it might be nice if there was an example html file that lets people do this to try it out.

Summary

I like it. It’s pretty solid and was easy to use, although at some points it was a little involved and you need to decide how you want to report results, set up that kind of environment, etc. Once you get everything in place though, it seems like it’s a cinch to use. Coming for JUnit, the learning curve is pretty shallow.

But I still like spec based frameworks more. :)

  • http://jspec.info TJ Holowaychuk

    Another great writeup. Interesting to have nice summaries of everything out there, gives me some inspiration for JSpec :) I like posting to a URI feature, would be hella simple with JSpec’s abstracted reporting I will have to try that out.

    I was also thinking about a hosted CI service / loads of browsers for testing so people dont need to go setting up or installing browsers on their server/laptop

  • http://blog.james-carr.org James Carr

    Hmmm… I actually think this would be a very good idea… CI in the cloud. :)

  • http://www.nczonline.net/ Nicholas C. Zakas

    I’m glad you enjoyed YUI Test. I can answer the question of it being actively developed because I’m actively developing it. :) There will continue to be development as long as I continue to receive feedback, and YUI Test has already been ported to YUI 3 (the YUI 3 version also includes a mocking utility). If you ever have feedback or suggestions, please feel free to contact me directly or start a conversation on ydn-javascript.

  • http://blog.james-carr.org James Carr

    Thanks for the reply Nicholas! The next part of this series (following the different TDD frameworks) is to cover different mocking frameworks/strategies in javascript, so I’ll definitely have to follow up with the mocking framework in the YUI3 version.

  • http://farmdev.com/ Kumar McMillan

    In all of these JavaScript unit testing frameworks I see (JsUnit, QUnit, etc) they all seem to depend too much on the web browser itself. This makes automation hard, it makes running tests on multiple browsers cumbersome, and it makes CI difficult. JsTestDriver – http://code.google.com/p/js-test-driver/ – takes a fresh approach whereby it treats each browser as a runtime environment that can live on any machine. You run tests from the command line; the script talks to a central server and runs tests simultaneously on all connected web browsers, reporting the result as text or xunit XML. I’ve been using it a fair bit and I wrote up a short article about it here: http://farmdev.com/thoughts/79/unit-testing-javascript-with-jstestdriver/

  • http://www.nczonline.net/ Nicholas C. Zakas

    Kumar – I think of JS Test Driver as a front end for the JavaScript unit testing frameworks. You still ultimately need code to be executed in the browser – JS Test Driver allows you to kick it off from a command line and other there are other tools that can do similar. I don’t think we’re necessarily comparing apples-to-apples on this one.

  • http://blog.james-carr.org James Carr

    Actually the ability to plug different testing frameworks into js-test-driver is one of the goals of the project. I am currently working with the source to get an architecture in place that allows the ability to write an adapter for different frameworks to plug them in.

    The first two frameworks on my list to try and get working with it is jspec and YUI Test. :)

  • http://www.agimatec.de/blog Simon Tiffert

    Because we use Selenium for our functional tests, we already had a setup for starting and controlling different browsers.
    We added YUI tests with the YUI Testmanager and are tracking the results on the last page. In this way we have a setup to get the results back of IE, Firefox, Opera to our CI server.

    Maybe interesting:
    http://www.agimatec.de/blog/2009/01/javascript-unit-tests-with-the-yui-testmanager/