Skip to main content

The Fundamentals of a JavaScript Test Suite

Posted by manning_pubs on February 7, 2013 at 5:18 AM PST



The Fundamentals of a JavaScript Test Suite

by John Resig and Bear Bibeault, authors of Secrets of the JavaScript Ninja

As important as a solid testing strategy is for all code, it can be crucial for situations where external factors have the potential to affect the operation of your code, which is exactly the case you're faced with in cross-browser JavaScript development. In this article based on chapter 2 of Secrets of the JavaScript Ninja, the authors show you how to construct a test suite to reliably run those tests.

The primary purpose of a test suite is to aggregate all the individual tests that your code base might have into a single location, so that they can be run in bulk, providing a single resource that can be run easily and repeatedly.

To better understand how a test suite works, it makes sense to look at how a test suite is constructed. Perhaps surprisingly, JavaScript test suites are really easy to construct. A functional one can be built in only about 40 lines of code.

You would have to ask, though, "Why would I want to build a new test suite?" For most cases, it probably isn't necessary to write your own JavaScript test suite. There are already a number of good-quality suites to choose from (as already shown). But building your own test suite can serve as a good learning experience, especially when looking at how asynchronous testing works.

The assertion

The core of a unit-testing framework is its assertion method, usually named assert(). This method usually takes a value—n expression whose premise is asserted—and a description that describes the purpose of the assertion. If the value evaluates to true and, in other words, is "truthy," the assertion passes; otherwise, it's considered a failure. The associated message is usually

logged with an appropriate pass/fail indicator.

A simple implementation of this concept can be seen in the next listing.

Listing 1 A simple implementation of a JavaScript assertion

<html>
  <head>
    <title>Test Suite</title>
    <script>

      function assert(value, desc) { 

        var li = document.createElement("li");

        li.className = value ? "pass" : "fail";

        li.appendChild(document.createTextNode(desc));

        document.getElementById("results").appendChild(li);

      } 


      window.onload = function() {
        assert(true, "The test suite is running.");

        assert(false, "Fail!"); 

      };
    </script>
   
    <style>
      #results li.pass { color: green; } 

      #results li.fail { color: red; } 

    </style>
  </head>

  <body>
    <ul id="results"></ul>

  </body>
</html>

The function named assert() (#1) is almost surprisingly straightforward. It creates a new

  • element containing the description, assigns a class named pass or fail, depending upon the value of the assertion parameter (value), and appends the new element to a list element in the document body (#4).

    The test suite consists of two trivial tests (#2): one that will always succeed, and one that will always fail.

    Style rules for the pass and fail classes (#3) visually indicate success or failure using colors.

    This function is simple, but it will serve as a good building block for future development.

    Test groups

    Simple assertions are useful, but they really begin to shine when they're grouped together in a testing context to form test groups.

    When performing unit testing, a test group will likely represent a collection of assertions as they relate to a single method in our API or application. If you were doing behavior-driven development, the group would collect assertions by task. Either way, the implementation is effectively the same.

    In our sample test suite, a test group in which individual assertions are inserted into the results is built. Additionally, if any assertion fails, then the entire test group is marked as failing. The output in the next listing is kept pretty simple

    AttachmentSize
    cueball1.png1.19 KB
    cueball2.png1.19 KB
    cueball3.png1.19 KB
    cueball4.png1.19 KB
    image009.jpg23.62 KB
    secrets001.png29.82 KB
    secrets002.jpg10.34 KB
    secrets003.jpg10.73 KB