Tools to unit test your JavaScript

Using QUnit, YUI Test, and JSTestDriver

Some developers may complain that JavaScript is difficult to test. With the current focus on the client side of web applications, however, unit testing JavaScript code is becoming essential. Now, you have tools to make sure your code is solid. In this article, learn about some of the most common tools for unit testing JavaScript: QUnit, YUI Test, and JSTestDriver. Code examples walk you through sample test cases.

Share:

Sebastiano Armeli-Battana, Software Engineer, Freelance

Sebastiono Armeli-Battana photoSebastiano Armeli-Battana is a Senior Software Engineer living and working in Melbourne. He has developed and designed applications using different programming languages (JavaScript, Java, Ruby) and he is really passionate around JavaScript and web development. He is the author of a jQuery plug-in called JAIL and he also enjoys speaking at conferences and writing technical articles. Sebastiano holds a Masters Degree in Software Engineering from the Polytechnic Institute of Milan.



10 April 2012

Also available in Chinese Russian Japanese

Introduction

Unit testing focuses on verifying that a module or a unit of code is working as designed or as expected. Some developers, who would rather spend time implementing new modules, consider writing test cases a waste of time. When dealing with large applications, however, unit testing actually saves time; it helps you track issues and lets you safely update your code.

Frequently used abbreviations

  • DOM: Document Object Model
  • HTML: HyperText Markup Language
  • JSTD: JSTestDriver
  • YUI: Yahoo! User Interface

In the past, unit testing was applied only to server-side languages. But, the increasing complexity in front-end components has increased the need for writing test cases for JavaScript code. The learning curve can be steep if you do not typically write tests for client-side scripting. Testing the user interface might require adjustments in your thought process. (And, some developers may still have a hard time believing that JavaScript is a proper programming language.)

In this article, learn how to use QUnit, YUI Test, and JSTestDriver to unit test JavaScript.

Download the source code for this article.


JavaScript unit testing

To illustrate JavaScript testing, this section analyzes a test case for a basic function in JavaScript. Listing 1 shows the function to be tested: add 3 (as a number) to the variable passed.

Listing 1. Source code (example1/script.js)
function addThreeToNumber(el){
    return el + 3;
}

Listing 2 contains the test case in a self-executing function.

Listing 2. Test case (example1/test.js)
(function testAddThreeToNumber (){
    var a = 5,
        valueExpected= 8;
    
    if (addThreeToNumber (a) === valueExpected) {
        console.log("Passed!");
    } else {
        console.log("Failed!");
    }
}());

After passing 5 to the function being tested, the test checks that the return value is 8. If the test is successful, Passed! is printed in the console of a modern browser; otherwise, Failed! appears. To run this test, you need to:

  1. Import the two script files in an HTML page that will act as a test runner, as shown in Listing 3.
  2. Open the page in your browser.
Listing 3. HTML page (example1/runner.html)
<!DOCTYPE html>
<html>
     <head>
         <meta http-equiv="Content-type" content="text/html; charset=utf-8">
         <title>Example 1</title>
         <script type="text/javascript" src="js/script.js"></script>
          <script type="text/javascript" src="js/test.js"></script>
     </head>
     <body></body>
</html>

Instead of using the browser console, you could print the results inside the page or inside pop-up windows generated by the alert() method.

Assertions, which are the core elements in test cases, are used to verify that a certain condition is satisfied. For example, in Listing 2, addThreeToNumber (a) === valueExpected is an assertion.

If you have a lot of test cases with many assertions, a framework comes in handy. The following sections highlight some of the most popular JavaScript unit testing frameworks: QUnit, YUI Test, and JSTestDriver.


Getting started with QUnit

QUnit, a unit testing framework similar to JUnit (Java programming), is used by the jQuery team to test the jQuery library. To use QUnit you need to:

  1. Download the qunit.css file and qunit.js file (see Resources).
  2. Create an HTML page containing specific tags that import the CSS and JavaScript files that you just downloaded.

Listing 4 shows a standard HTML runner for QUnit.

Listing 4. HTML runner (qunit/runner.html)
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>QUnit Test Suite</title>
        <link rel="stylesheet" href="css/qunit.css" type="text/css" media="screen">
        <script type="text/javascript" src="js/lib/qunit.js"></script>
    </head>
    <body>
        <h1 id="qunit-header">QUnit Test Suite</h1>
        <h2 id="qunit-banner"></h2>
        <div id="qunit-testrunner-toolbar"></div>
        <h2 id="qunit-userAgent"></h2>
        <ol id="qunit-tests"></ol>
        <div id="qunit-fixture">test markup</div>
    </body>
</html>

Assume you have two functions in charge of converting the temperature from Celsius to Fahrenheit and vice versa. Listing 5 shows the script for doing the conversions.

Listing 5. Conversions (qunit/js/script.js)
function convertFromCelsiusToFahrenheit(c){
    var f = c * (9/5) + 32;
    return f;
}

function convertFromFahrenheitToCelsius(f){
    var c = (f - 32) * (5/9);
    return c;
}

Listing 6 shows the respective test cases.

Listing 6. Test cases (qunit/js/test.js)
module ("Temperature conversion")

test("conversion to F", function(){
    var actual1 = convertFromCelsiusToFahrenheit(20);
    equal(actual1, 68, ?Value not correct?);
	
    var actual2 = convertFromCelsiusToFahrenheit(30);
    equal(actual2, 86, ?Value not correct?);
})

test("conversion to C", function(){
    var actual1 = convertFromFahrenheitToCelsius(68);
    equal(actual1, 20, ?Value not correct?);

    var actual2 = convertFromFahrenheitToCelsius(86);
    equal(actual2, 30, ?Value not correct?);
})

Test cases in QUnit are defined by the test() method. The logic is contained in the second argument passed into the function. In Listing 6, the two tests are named conversion to F and conversion to C. Each test contains two assertions. The assertions used in the tests leverage the equal() method. The equal() function lets you compare the expected value with the actual value coming from the function under test. The third argument in the equal() method is the message displayed in case of failure.

Tests can also be organized into modules through the module() function. In Listing 6, the module Temperature conversion holds the two tests.

To run the tests:

  1. Include the source code and the test file in the HTML runner, as shown in Listing 7.
  2. Open the HTML page in a browser.
Listing 7. Including script.js and test.js in the runner
...
<script type="text/javascript" src="js/script.js"></script>
<script type="text/javascript" src="js/test.js"></script>
...

Figure 1 shows how QUnit displays the results in a browser (Firefox).

Figure 1. QUnit results
A screen shot showing that 4 out of 4 tests were successful.

The assertions in Listing 6 use the equal() method, but that is not the only assertion provided by QUnit. Other assertions offered by QUnit include ok() or strictEqual(). Listing 8 shows those methods in action.

Listing 8. More assertions
module ("Other assertion");
test("assertions", function(){
    ok(true);
    ok(3);
    strictEqual("c", "c");
    equal (3, "3");
});

The ok() function checks that the first parameter is true; strictEqual() verifies that the first parameter is strictly equal to the second one. Behind the scenes, strictEqual() uses the === operator and equal() uses the == operator.

QUnit also provides useful information if your tests fail. Change the code in Listing 8 to the code in Listing 9 so the last assertion will fail.

Listing 9. Error on the last assertion
module ("Other assertion");
test("assertions", function(){
    ok(true);
    ok(3);
    strictEqual("c", "c");
    strictEqual (3, "3");
});

Figure 2 shows what QUnit returns from the code in Listing 9.

Figure 2. QUnit result — last test failed
Screen shot showing that 7 out of 8 tests passed and 1 failed.

The result is quite detailed, and it's easy to trace what is different between the expected value and the actual value in the last assertion.

Another important QUnit feature allows you to execute commands before, or after, all tests inside a module are executed. The module() function accepts the setup() and teardown() callbacks as a second parameter. Update Listing 6 using the setup() function, as shown in Listing 10.

Listing 10. setup() (qunit/js/test-setup.js)
module ("Temperature conversion", {
    setup : function() {
        this.celsius1 = 20;
        this.celsius2 = 30;
		
        this.fahrenheit1 = 68;
        this.fahrenheit2 = 86;
    }
});
test("conversion to F", function(){
    var actual1 = convertFromCelsiusToFahrenheit(this.celsius1);
    equal(actual1, this.fahrenheit1);
	
    var actual2 = convertFromCelsiusToFahrenheit(this.celsius2);
    equal(actual2, this.fahrenheit2);
});
test("conversion to C", function(){
    var actual1 = convertFromFahrenheitToCelsius(this.fahrenheit1);
    equal(actual1, this.celsius1);
	
    var actual2 = convertFromFahrenheitToCelsius(this.fahrenheit2);
    equal(actual2, this.celsius2);
});

The example moves the values used in the assertions in the setup section to avoid using those values in the logic of the tests.

QUnit also offers support for asynchronous tests through the asyncTest() function, which is a very useful feature if you're dealing with Asynchronous JavaScript and XML (Ajax). In this context, the expect() function handily lets you verify the number of assertions run in a test.


YUI Test: An independent module for unit testing

YUI Test, a component inside the YUI library (Yahoo!), is an extensive and complete unit testing framework. To get started with YUI Test, you need to:

  1. Import the YUI seed into the HTML runner, as follows.
    <script src="http://yui.yahooapis.com/3.4.1/build/yui/yui-min.js"></script>

    As the code reflects, the example uses YUI Test version 3.
  2. In the test script file, instantiate the YUI function. Load the needed modules, test and console, as shown in Listing 11.
Listing 11. Load test and console YUI modules
YUI().use("test", "console", function (Y) {
     // Test cases go here
});

The test module is clearly necessary for testing purposes. The console module is not mandatory, but the example will use it for printing the results. The test cases will go inside the callback, with the global Y instance as the argument.

YUI Test uses the Y.Test.Case() constructor to instantiate a new test case and the Y.Test.Suite() constructor to instantiate a test suite. A test suite, similarly to JUnit, contains several test cases. You can add test cases to a test suite using the add() method.

Let's re-test the source code in Listing 5 using YUI test. Listing 12 shows how to create a suite and a test case for the test.

Listing 12. Test suite and test case
YUI().use("test", "console", function (Y) {
	
     var suite = new Y.Test.Suite("Temperature conversion suite");

     //add a test case
     suite.add(new Y.Test.Case({
         name: "Temperature conversion?
     )); 
});

Listing 12 generated a suite called Temperature conversion suite and a test case called Temperature conversion. Now you can write the test methods inside the object literal passed as an argument into the Y.Test.Case constructor, as shown in Listing 13.

Listing 13. Test cases with test methods
suite.add(new Y.Test.Case({
    name: "Temperature conversion",

    setUp : function () {
        this.celsius1 = 20;
        this.celsius2 = 30;
		
        this.fahrenheit1 = 68;
        this.fahrenheit2 = 86;
    },

    testConversionCtoF: function () {
        Y.Assert.areEqual(this.fahrenheit1,         
convertFromCelsiusToFahrenheit(this.celsius1));
        
        Y.Assert.areEqual(this.fahrenheit2, 
convertFromCelsiusToFahrenheit(this.celsius2));
    },
		
    testConversionFtoC: function () {
        Y.Assert.areEqual(this.celsius1,
convertFromFahrenheitToCelsius(this.fahrenheit1));
			
        Y.Assert.areEqual(this.celsius2, 
convertFromFahrenheitToCelsius(this.fahrenheit2));
    }
}));

You likely noticed in Listing 13 that:

  • The setUp() method is available. YUI Test provides setUp() and tearDown() methods at the test case and test suite level.
  • Test method names start with the word test, and they contain assertions.
  • The example uses the Y.Assert.areEqual() assertion type, which is similar to the equal() function in QUnit.

    YUI Test offers a wide range of methods for assertions, such as:

    • Y.Assert.areSame(), which is the equivalent of strictEqual() in QUnit.
    • Data type assertions (Y.Assert.isArray(), Y.Assert.isBoolean(), Y.Assert.isNumber(), and so on).
    • Special value assertions (Y.Assert.isFalse(), Y.Assert.isNaN(), Y.Assert.isNull(), and so on).

To launch tests in YUI, use the Y.Test.Runner object. You need to add suites or test cases to this object and then call the run() method to run the tests. Listing 14 shows how to run the tests created in Listing 13.

Listing 14. Run YUI tests
Y.Test.Runner.add(suite);
Y.Test.Runner.run();

By default, the results are printed in the console of your browser (if your browser supports a console). A nicer way to print results is with the Yahoo! Console component. To use theYahoo! Console component, you need to adopt the Y.Console constructor and bind the console to a DOM element of the HTML runner, as shown in Listing 15.

Listing 15. Yahoo! Console
var console = new Y.Console({
    verbose: true,
    newestOnTop: false,
    width: "600px"
});

console.render('#testLogger');

Listing 15 shows how you can configure the console with several parameters. The console is rendered inside the DOM element with the id equal to testLogger.

The HTML runner needs to be updated. Add the DOM element referenced by the console, as in Listing 16.

Listing 16. HTML runner updated to support Yahoo! Console
<body class="yui3-skin-sam">
     <div id="testLogger"></div> 
</body>

The example set a class for the <body> named yui3-skin-sam. The class is responsible for defining a skin for the console.

Figure 3 shows the console after you've run the tests.

Figure 3. YUI Test results
Screen capture of the log console showing the results of each test.

Easy testing with JSTestDriver

With the powerful JSTestDriver (JSTD) tool you can run JavaScript from the command line in multiple browsers. JSTD comes with a JAR file that lets you start a server, capture one or multiple browsers, and run tests in the browsers. You don't need an HTML runner, as with the two frameworks discussed above, but you do need a configuration file. Listing 17 shows a configuration file.

Listing 17. Configuration file (jsTestDriver.conf)
server: http://localhost:4224
load:
  - js/src/*.js
test:
  - js/test/*.js

The configuration file is written in YAML, which is a great format for configuration files. It contains information about the server to launch and the location of the source code and test files.

To execute tests with JSTD:

  1. Launch the test server. From the command line, go into the folder where the file jsTestDriver.jar is saved and launch the following command:
    java -jar JsTestDriver-1.3.3d.jar -port 4224

    The port specified in Listing 17 should be the same as the one specified in the configuration file. By default, JSTD looks for the jsTestDriver.conf file in the same directory where the JAR file resides.

  2. Register one or multiple browsers to the server by copying and pasting the URL http://localhost:4224/capture in the browsers under test.

Test the same source code you used for the previous examples (Listing 5), but this time use the JSTD syntax. Listing 18 shows how to convert the test cases from Listing 10 for QUnit and Listing 14 for YUI Test.

Listing 18. JSTD tests
TestCase("Temperature conversion", {
    setUp : function () {
        this.celsius1 = 20;
        this.celsius2 = 30;
	
        this.fahrenheit1 = 68;
        this.fahrenheit2 = 86;
    },

    testConversionCtoF: function () {
        assertSame(this.fahrenheit1, convertFromCelsiusToFahrenheit(this.celsius1));
        assertSame(this.fahrenheit2, convertFromCelsiusToFahrenheit(this.celsius2));
    },
	
    testConversionFtoC: function () {
        assertSame(this.celsius1, convertFromFahrenheitToCelsius(this.fahrenheit1));
        assertSame(this.celsius2, convertFromFahrenheitToCelsius(this.fahrenheit2));
    }
});

The code in Listing 18 is not very different from the YUI version. JSTD uses the TestCase() function to define a test case. You can define the test methods using an inline declaration, as in Listing 18, or you can extend the prototype of the TestCase instance. The SetUp() and tearDown() methods are available for each test case.

To run the tests, simply launch the following command:

java -jar JsTestDriver-1.3.3d.jar --tests all

Figure 4 shows the output you get at a terminal.

Figure 4. Results from JSTD tests
Command line results showing which tests passed and which failed.

The tests are passing in all of the browsers previously captured (Chrome 15, Safari 5, and Firefox 7).

JSTD also integrates well with your preferred continuous integration system to be part of your continuous build. It offers integration with IDEs such as Eclipse (plug-in) or TextMate (bundle).


Conclusion

With the current focus on the client side of web applications, unit testing JavaScript code becomes essential. There are a few frameworks that can help you accomplish the task, and this article examined three of the most popular frameworks: QUnit, YUI Test, and JSTestDriver.

  • QUnit, which is very straightforward, is a good framework to start with.
  • YUI Test is a complete tool suitable for people familiar with the YUI library.
  • JSTestDriver is a great framework for running your tests in multiple browsers.

Download

DescriptionNameSize
Article source codeIBM_UnitTestingJS.zip54KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=808458
ArticleTitle=Tools to unit test your JavaScript
publish-date=04102012