Javascript Unit Test Environment (JUTE) Now Open!

Ready to kick your Javascript Unit Testing up to the next level?  Or even to the very first level?  

Javascript Unit Testing suffers from lack of standardization.  There is no universal 'JUnit' or 'PHPUnit' to 'rule them all'.  There are lots of very good Javascript testing frameworks available, especially YUI3's test framework. But there's not a lot beyond that, so now JUTE is throwing its hat into the Javascript 'do-it-all' testing environments showcase showdown!

YUI3's framework provides the foundation for writing unit tests, collecting them into test cases and suites, and even mocking objects.  And that's great.  But we can do more, and do it easier!

The Javascript Unit Test Environment (JUTE) combines the YUI3 testing framework, automatic dynamic code coverage, a web UI, a command line interface, seamless client- and server-side Javascript testing and 3 testing back ends (Capture, Selenium, and V8) in one easy to setup and use package.  This is all provided with very little setup by you - just like we all like it!

Since JUTE can be fully controlled from the command line it plays very nicely within your build environment - gmake, ant, hudson, whatever!

JUTE is a recently open-sourced internal project. JUTE is used extensively by Yahoo! Mail and other properties within Yahoo!. Yahoo! Mail has thousands of individual unit tests all running through JUTE as part of Mail's build process.

Anatomy of a client-side Javascript Unit Test

First let's look at what a simple client-side Javascript Unit Test looks like.  Here's some simple code we want to test (note JUTE does NOT require that code you want to test be written using YUI3!).

In 'mySum.js':

function mySum() {

    var result = 0, i = 0;

    if (typeof arguments[0] == 'object' && arguments[0].length) {

        for (;i < arguments[0].length; i++) {

            result += arguments[0][i];

        }

    } else {

        for (;i  < arguments.length; i++) {

            result += arguments[i];

        }

    }

    return result;

}

A slightly bizarre 'sum' function - does it work?  Let's write a test for it using JUTE!  JUTE builds on top of the YUI3 test module, so we need to use that to write our tests.  Again, the code you are testing does NOT need to use or have anything to do with YUI3 (although it can!) - only the TESTING code needs to utilize the YUI3 test stuff - which is good test stuff to use regardless!

In 'testMySum.js':

YUI({
    logInclude: { TestRunner: true },

    gallery:    'gallery-2011.06.22-20-13'

}).use('gallery-jute', function(Y) {

    var suite = new Y.Test.Suite('mySum');

    suite.add(new Y.Test.Case({

        name:'simple sums',

        testTwoNumbers: function() {

            Y.Assert.areEqual(mySum(5, 5), 10);

        },

        testArray: function() {

            Y.Assert.areEqual(mySum([5, 5]), 10);

        }

    }));

    Y.Test.Runner.add(suite);

    Y.UnitTest.go();

});

So those are two reasonable tests for our 'mySum' function.  Now we need an HTML file to pull it all together.

In 'testMySum.html':

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html lang="en">

    <head>

         <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

    </head>

    <body class="yui3-skin-sam">

        <div id="log" />

        <script src="https://yui-s.yahooapis.com/3.3.0/build/yui/yui-min.js"></script>

        <script src="mySum.js?coverage=1"></script>

        <script src="testMySum.js"></script>

    </body>

</html>

OK so we've got all we need for a basic client-side Javascript Unit Test.  We have what we want to test (mySum.js), our test code (testMySum.js) and finally an HTML file to pull it all together (testMySum.html).  We could be done here as most if not all Javascript test frameworks stop here. But using JUTE we are going to kick it up a notch!

By making slight additions to 'vanilla' YUI3 test code we gain the full power of JUTE. We have added the 'gallery-jute' module (which also pulls in YUI3's 'test' module for you) in testMySum.js AND note the '?do_coverage=1' query string I snuck in testMySum.html on the mySum.js file!  This will transparently tell JUTE that IF you want to run these tests with code coverage THIS is the file we want code coverage for - which, surprise surprise, is the file we're testing.

As a special bonus the unit test will still run whether you use JUTE or not.

Anatomy of a server-side Javascript Unit Test

Our 'mySum' function can clearly just as easily be run on the server side using NodeJS.  What would a unit test for server-side Javascript look like?

First off our mySum.js file will change slightly because we want to utilize the NodeJS 'require' feature.

In 'myServerSum.js':

module.exports = {

    mySum: function() {

    var result = 0, i = 0;

    if (typeof arguments[0] == 'object' && arguments[0].length) {

        for (;i < arguments[0].length; i++) {

            result += arguments[0][i];

        }

    } else {

        for (;i  < arguments.length; i++) {

            result += arguments[i];

        }

    }

    return result;

}

};

Here we just wrapped our 'mySum' function into a module.exports declaration.  Now our tests will change slightly to use 'require' instead of relying on an HTML file to link everything together.

In 'testServerMySum.js':

YUI({

    logInclude: { TestRunner: true },

}).use('jute', function(Y) {

    var suite = new Y.Test.Suite('mySum'),

          mySum = require('myServerSum', true).mySum;

    suite.add(new Y.Test.Case({

        name:'simple sums',

        testTwoNumbers: function() {

            Y.Assert.areEqual(mySum(5, 5), 10);

        },

        testArray: function() {

            Y.Assert.areEqual(mySum([5, 5]), 10);

        }

    }));

    Y.Test.Runner.add(suite);

    Y.UnitTest.go();

});

No not a lot of (visible!) magic here - we just 'require' the myServerSum javascript & pull out the 'mySum' function. We have made only ONE change to utilize JUTE - note:

mySum = require('myServerSum', true).mySum;

In 'testServerMySum.js'!  Similarly to '?do_coverage=1' in the client-side example, this tells JUTE that IF we run unit tests wanting code coverage THIS is the file to apply code coverage to.  And, surprise surprise, this is the file we are testing. This is the only addition required by JUTE! If you don't want code coverage you do not even need to do that - it cannot get any lazier!

Ready To Rumble

At this point JUTE now gives you the flexibility to run your tests via a web UI or a command-line interface.  JUTE will persist not only your unit test output in JUnit XML format, but will also automatically generate pretty HTML for code coverage AND give you the option of running these tests in either Captured browsers, on a Selenium RC or Grid host, or directly thru V8.  Of course the NodeJS version of mySum can ONLY be run thru the V8 backend.  However in this case the client-side version CAN be run thru any of the 3 backends.

So you've actually done all the hard parts already:  for client-side Javascript testing you have the file you want to test, the file with your tests in it, and an HTML file tying them all together.  For server-side Javascript you've got the file you want to test and file containing all of your test code.

Now let's sit back and run it all thru JUTE and start living the good life!

Installing JUTE

First you need to install the thing - JUTE requires only the current versions of NodeJS (0.4.10) and npm (1.x).  When you are ready:

% npm install jute -g

Starting, stopping, and restarting JUTE is equally easy:

    % npm start jute

    % npm stop jute

    % npm restart jute

Basic Configuration

JUTE is a standalone webserver so before you start it you need to tell it where your document root is, where your test files are, and where it should put its output.  You can also give JUTE another port to run on and some other configuration.  Regardless all configuration is accessible via npm config variables:

% npm config set jute:docRoot /var/htmlroot

% npm config set jute:testDir test

% npm config set jute:outputDir output

% npm config set jute:port 8080

SO JUTE will look for all of your *.html 'glue' test file in docRoot + testDir (/var/htmlroot/test) in this example and output test results and code coverage information into docRoot + outputDir (/var/htmlroot/output) in this case.  AND JUTE will listen on port 8080.

If you make ANY changes to JUTE variables you MUST restart JUTE for your changes to take effect:

% npm restart jute

Now that JUTE is configured you are ready to run some tests the JUTE way!

Backends

Before we run our tests let's quickly divert and discuss the testing 'backends' available with JUTE.

Capture

Capture mode runs tests on all 'captured' browsers.  A 'captured' browser is simply one that is currently pointing to http://<JUTE_SERVER>:<JUTE_PORT>/  All such browsers (desktop, mobile, whatever) are 'captured' by JUTE and any submitted 'capture' unit tests will run on ALL currently captured browsers in parallel. This allows you to test your code in almost any browser, from desktop to mobile.

Selenium

JUTE plays nicely with Selenium RC and Grid.  The Selenium back end will ship all requested tests to a Selenium RC or Selenium Grid instance.  Just specify the Selenium browser specification (e.g. '*firefox') and off your tests go.
The Selenium back end is only available via JUTE's command-line interface.

V8

Some client-side Javascript and of course and all server-side Javascript will run through the V8 back end.  The V8 back end is only available via JUTE's command-line interface.

Running Your Tests

Hey we are just about ready to run our awesome unit tests the JUTE way!  You have two choices to kick off your unit tests, via the WebUI or the command-line interface.  Note ONLY Capture tests can be run via the web UI - Selenium and V8 back ends are only accessible via JUTE's command-line interface.  See below for details!

WebUI

JUTE comes with a semi-snazzy webui available here:

http://<JUTE_SERVER>:<JUTE_PORT>/

This will bring up a lot of information - the most useful being the list of unit tests available to be run assuming you set up docRoot and testDir correctly!
From the WebUI you are able to run any/all tests in Capture mode.  You can also see output from ALL modes in the 'results' pane on the right.

Command-Line Interface

This is where things get especially interesting.  From the command-line interface you can run tests in any of the 3 back ends with code coverage or not.  Let's look at some examples!  NOTE!!!!!!  All testfiles are specified RELATIVE to <docRoot>/<testDir>!!

% jute_submit_test --test myTestSum.html

This is as simple as it gets - submits that 1 HTML file to be unit tested in capture mode.

% jute_submit_test --test myTestSum.html --v8

Same thing BUT use the V8 backend.

% jute_submit_test --test myTestSum.html --sel_host our.selenium.rc.com -sel_browser '*firefox'

How about multiple tests?

% jute_submit_test --test myTestSum.html --test myOtherTest.html

Again use the '--v8' or '--sel_host' flags for those back ends.

Even

% jute_submit_test --test -

To read test files from STDIN is an option.

How about code coverage?

% jute_submit_test --test myTestSum.html?do_coverage=1 --v8

Simply stick the '?do_coverage=1' querystring at the end any test to enable code coverage.

Now JUTE will go off and run you test(s) using the specified back end and will dump output into <docRoot>/<outputDir>.  JUTE will also generate pretty HTML for code coverage if requested - all available for viewing via the webui - EVEN Selenium and V8 tests!

More Information

I hope you've seen writing, running, and analyzing Javascript unit tests CAN be fun AND easy! More examples are available at the JUTE homepage - so check it out!

Don't let your client- or server-side Javascript get out of control!  JUTE makes it easy and seamless to get started so kick your Javascript Unit Testing up to the next level today!