Javascript Unit Testing for the Conscientious Developer

Javascript

Nothing says, “I am a conscientous developer!” more than properly Unit Testing all of your code.

…yes, even your client-side stuff.

…yes, that means Javascript.

Let me help you get started with this noble task…

Unit Testing Requirements

Unit Testing Javascript only requires a couple of things …

  1. A Javascript Interpretter w/ a DOM (Document Object Model) API that follows the guidlines set forth by the W3C.
  2. A Unit Testing Framework.

Javascript Interpretters

Javascript Interpretters come in two flavors …

Web Browser Interpretters

Javascript Interpretters are not commonly found in the wild – they are built into a Web Browser. Javascript interpretters hosted within a Web Browser expose an API to interact with the DOM.

Command-Line Interpretters

There are, of course, exceptions. Rhino, V8, and Windows Script Host run outside of a Web Browser on a command-line. Command-Line Javascript Interpretters are not bound to a Web Browser and as such they do not have a DOM API – as there is no DOM.

Unit Test Frameworks

There are many different flavors of Unit Test Frameworks. When running a Unit Test each of these Frameworks follows one of two methodologies …

In-Browser

An “In-Browser” Unit Test is run directly within a Web Browser. The Unit Test leverages the Web Browser’s Interpretter and DOM API. The Test Result is displayed as a web page and must be visually inspected – or programatically scraped. This can make automation difficult.

JsUnit and QUnit are two commonly used In-Browser Test Frameworks.

Headless

A “Headless” Unit Test is run using a Command-Line Javascript Interpretter. “Headless” means “No monitor”. The Test Result can theoretically be piped anywhere. This makes it easier to automate.

PhantomJs is a Headless Test Framework that utilizes JavaScriptCore and WebKit. JavaScriptCore is the Javascript Interpretter used by Safari. WebKit is the DOM API / Rendering Engine used by Safari and Chrome.

Keep in mind that although Headless Unit Tests are run outside of a Web Browser they are not really Web Browser agnostic. A Headless Unit Test Framework is only as good as the libraries that it depends on. For example, a passing Unit Test in PhantomJs could theoretically fail in Internet Explorer or Firefox simply because the later use entirely different Javascript and DOM API’s.

How to Write an In-Browser Unit Test

Moving forward I am going to show you how to use QUnit to write In-Browser Unit Tests. Other In-Browser Testing Frameworks are very similar.

Components of a QUnit Unit Test

Writting QUnit Unit Tests requires three things …

  1. QUnit Test Runner (.html)
  2. Unit Tests (.js)
  3. Production Scripts (.js)

QUnit Test Runner

A Test Runner is just a Web Page that links together your Unit Tests and your Production Scripts. When opened in a Web Browser the Test Runner will run the Unit Tests and display the results. Mocked-up HTML Markup can be applied to the Test Runner if the Production Scripts being tested require it. The Mocked-up HTML is reset after each Unit Test is run.

This is what the Test Runner’s markup looks like …

<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8" />
   <title>QUnit Test Suite</title>

   <!-- QUnit Dependencies -->
   <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.5.0.css">
   <script src="http://code.jquery.com/qunit/qunit-1.5.0.js" type="text/javascript"></script>
   <script src="http://code.jquery.com/jquery-1.5.min.js" type="text/javascript"></script>

   <!-- Production Script(s) . -->
   <script src="myScript.js"></script>

   <!-- Unit Test(s) --->
   <script src="myUnitTest.js"></script>

</head>
<body>
   <div id="qunit"></div>
   <div id="qunit-fixture">

      <!-- Mocked-up HTML Markup. -->

   </div>
</body>
</html>
QUnit Test Runner

This is what the Test Runner looks like in a Web Browser …

QUnit Test Runner

Unit Tests

A Unit Test verifies the functionality of Production Scripts. As your Production Scripts change during development it is the Unit Test’s job to verify that proper functionality is maintained. Each Unit Test invokes a function exposed by your Production Scripts and then Asserts that the outcome is correct. The Assertion could be as simple as verifying a return value or as complicated as verifying that the DOM has been modified apporpriately. Unit Tests are grouped into Modules. Multiple Modules can exist within a single Javascript file. A Unit Test can have any number of Assertions. The number of expected Assertions must be declared.

This is a simple example of a Unit Test. No Production Script is invoked – the Unit Test just asserts that “Hello” is not equal to “World”. “MyUnitTest – Group 1” is an arbitrary name to associate with the Unit Test. The name is displayed in the Test Runner when the Unit Test is run. “1” corresponds to the number of Assertions that are performed by the Unit Test. QUnit supports a handful of different Assertions

QUnit.module( "MyUnitTest - Group 1",
{  setup     : function() {},
   teardown  : function() {}
});

QUnit.test( "test()", 1, function() {

   notEqual("Hello","World");

});
Sample QUnit Unit Tests

Production Scripts

These are the scripts that you want to test. They should be identical to whatever gets deployed in your Production environment.

A Simple Unit Test

If you’re lucky your Production Scripts do not have any external dependencies and do not interact with the DOM.

For example, on a recent project I needed the ability to move an element in an Array from one index to another. To do this I extended the Javascript Array object with two functions – move() and indexOf().

The Production Scripts look like this. First, I check to see if the functions already exist for the Array object. If they don’t I simply add them …

if (!Array.prototype.move) {

   // Description : Moves an element in an array.
   // Params:       old_index - The old index of element.
   //               new_index - The new index of element.
   // http://stackoverflow.com/questions/5306680/move-an-array-element-from-one-array-position-to-another
   Array.prototype.move = function (old_index, new_index) {
      if (new_index >= this.length) {
         var k = new_index - this.length;
         while ((k--) + 1) {
            this.push(undefined);
         }
      }
      this.splice(new_index, 0, this.splice(old_index, 1)[0]);
      return this; // for testing purposes
   };
}

if (!Array.prototype.indexOf) {

   // Description : Finds the index of an object in the specified array
   // Params:       obj - The object.
   //                    start - The index to start looking for the object in the aray.
   // http: //stackoverflow.com/questions/1744310/how-to-fix-array-indexof-in-javascript-for-ie-browsers
   Array.prototype.indexOf = function (obj, start) {
      for (var i = (start || 0), j = this.length; i < j; i++) {
         if (this[i] === obj) { return i; }
      }
      return -1; 
   }
}
Extensions.js

As for Unit Testing there are two things that I want to verify. First, I want to make sure that the Production Scripts extended the Array object. Here is what the Unit Test for move() looks like …

QUnit.module(	"Extensions",
{    setup     : function() {},
      teardown  : function() {}
});

QUnit.test( "move() extends Array", 2, function() {
   notEqual(Array.prototype.move, undefined);
   notEqual(Array.prototype.move, null);
});
Extensions.UnitTest.js

Next, I want to make sure that the new Array functions work correctly. To do this I simply create an array, invoke the new Array functions against it, and then use QUnit Assertions to verify the result …

QUnit.module( "Extensions",
{    setup   : function() {},
     teardown: function() {}
});

QUnit.test( "Array.move to first", 5 , function() {

   var array = [ "Homer", "Bart", "Maggie", "Lisa", "Marge" ];

   array.move(4,0);

   equal( array[0], "Marge" );
   notEqual( array[1], "Marge" );
   notEqual( array[2], "Marge" );
   notEqual( array[3], "Marge" );
   notEqual( array[4], "Marge" );
});

QUnit.test( "Array.move to last", 5 , function() {

   var array = [ "Homer", "Bart", "Maggie", "Lisa", "Marge" ];

   array.move(0,4);

   equal( array[4], "Homer" );
   notEqual( array[3], "Homer" );
   notEqual( array[2], "Homer" );
   notEqual( array[1], "Homer" );
   notEqual( array[0], "Homer" );
});

QUnit.test( "Array.move to arbitrary", 5 , function() {

   var array = [ "Homer", "Bart", "Maggie", "Lisa", "Marge" ];

   array.move(2,3);

   equal( array[3], "Maggie" );
   notEqual( array[0], "Maggie" );
   notEqual( array[1], "Maggie" );
   notEqual( array[2], "Maggie" );
   notEqual( array[4], "Maggie" );
});
Extensions.js

The QUnit Test Runner links everything together. Notice that that the Unit Tests are referenced after the Production Scripts. The QUnit Test Runner loads and processes scripts in order of their appearance. It’s important to not reference your Unit Tests first as the Production Scripts that they are testing may not be loaded yet …

<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8" />
   <title>QUnit Test Suite</title>

   <!-- QUnit Dependencies -->
   <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.5.0.css">
   <script src="http://code.jquery.com/qunit/qunit-1.5.0.js" type="text/javascript"></script>

   <!-- JQuery -->
   <script src="http://code.jquery.com/jquery-1.5.min.js" type="text/javascript"></script>
   <script src="http://code.jquery.com/ui/1.8.13/jquery-ui.min.js" type="text/javascript"></script>

   <!-- Your Production Script(s) -->
   <script src="Extensions.js"></script>

   <!-- Your Unit Test Script(s) --->
   <script src="Extensions.UnitTest.js"></script>

</head>
<body>
   <div id="qunit"></div>

   <div id="qunit-fixture">
      <!-- The HTML Markup that your Production Script(s) Target -->
   </div>

</body>
</html>
TestRunner.html

This is what the Test Runner looks like in a Web Browser when running …

Simple Unit Test Runner

Click here to download this example.

A (Not So Simple) Unit Test

Unit Testing becomes more complicated when the Production Scripts manipulate the DOM and/or reference external API’s.

For example, let’s pretend that Marketing has provided the following requirements for a new Inventory Management Web Application …

  1. The application allows the End-User to browse Inventory by selecting a Category.
    • Selecting a new Category will retrieve the specified Inventory [from the web server].
    • The Inventory will be bound to a Table.
  2. The application allows the End-User to add new Inventory.
    • To do this the End-User clicks on an “Add Inventory” button.
    • This will display a Pop-up Dialog.
    • The Pop-up Dialog contain SKU, Name, and Quantity fields and a “Submit” button.
    • Clicking the “Submit” button will save the new Inventory [to the web server].

A Web Designer has provided the HTML Markup and CSS for the application. This is a picture of what the web application looks like in a Web Browser …

Inventory Management Screenshot

And this is the HTML Markup …

<!DOCTYPE html>
<html>
<head>
   <link rel="stylesheet" href="InventoryManagement.css">
</head>
<body>
   <div class="manage-inventory">

      <select id="selectInventoryCategory">
         <option value="" selected="selected">(Select a Category)</option>
         <option value="Drinks">Drinks</option>
         <option value="Food">Food</option>
         <option value="Dessert">Desssrt</option>
      </select>

      <table id="tableInventory">
         <thead>
            <tr>
               <th>SKU</th>
               <th>Name</th>
               <th>Quantity</th>
            </tr>
         </thead>
         <tbody>
            <tr>
               <td colspan="3" >No Inventory</td>
            </tr>
         </tbody>
      </table>

      <button id="buttonShowAddInventory" type="button">Add Inventory</button>

      <form id="formAddInventory" class="hidden" action="http://localhost/Merchandise/UpdateMerchandise" method="post">
         <fieldset>
            <input type="text" id="inputSKU" name="sku">
            <label for="inputSKU">SKU</label>
            <input type="text" id="inputName" name="name">
            <label for="inputName">Name</label>
            <input type="text" id="inputQuantity" name="quantity">
            <label for="inputQuantity">Quantity</label>
         </fieldset>
      </form>
   </div>
</body>
</html>
InventoryManagement.html

We have been tasked with creating Production Scripts and corresponding Unit Tests to support the required functionality. To do this we’re going to take a Test-Driven approach. Specifically, we’re going to …

  1. Stub out the Production Scripts.
  2. Create the Test Runner.
  3. Create the (Failing) Unit Tests.
  4. Finish the Production Scripts (to Satisfy the Failing Unit Tests).

Stub Out Production Scripts

As a rule of thumb it is very important to keep your Production Script functions as specific as possible. The more complicated the function the more difficult the Unit Test will be to write. You do not want a function that singlehandedly scrapes the DOM, performs a JQuery Ajax POST, and then updates the DOM. Instead, you will want to break down the function into discrete operations.

The application’s functionality can be broken into the following stubbed out functions …

function selectInventoryOnChange (event) {
   // TODO : Get Category from selectInventoryCategory <select> and invoke getInventoryFromServer().
}

function getInventoryFromServer ( inventoryCategory ) {
   // TODO : Make AJAX GET Request to Web Server for Category and invoke refreshInventoryTable() with retreived Data.
}

function refreshInventoryTable ( jsonInventoryData ) {
   // TODO : Refresh <table> with Inventory Data.
}

function showAddInventoryForm () {
   // TODO : Display the formAddInventory <form> in a JQuery UI Dialog.
}

function addNewInventoryClick () {
   // TODO : Retreive data from formAddInventory <form>, invoke addNewInventory(), and hide JQuery UI Dialog.
}

function addNewInventory ( formData ) {
   // TODO : Make AJAX POST to Web Server with <form> data.
}
InventoryManagement.js (Stubbed Out)

As these functions are going to be leveraged by our Unit Tests and the web application we should apply some best practices to make them more portable …

Production Script Best Practices

Namespacing

First, lets encapsulate the functions into a Namespace. Global functions are a no-no as the can overwrite (or be overwritten) by any other Javascript that your application might reference …

var ScottsJewels = ScottsJewels || {};
ScottsJewels.InventoryManagement = ScottsJewels.InventoryManagement || {};

ScottsJewels.InventoryManagement.selectInventoryOnChange = function (event) {
   // TODO : Get Category from selectInventoryCategory <select> and invoke getInventoryFromServer().
}

ScottsJewels.InventoryManagement.getInventoryFromServer = function ( inventoryCategory ) {
   // TODO : Make AJAX GET Request to Web Server for Category and invoke refreshInventoryTable() with retreived Data.
}

ScottsJewels.InventoryManagement.refreshInventoryTable = function( jsonInventoryData ) {
   // TODO : Refresh <table> with Inventory Data.
}

ScottsJewels.InventoryManagement.showAddInventoryForm = function () {
   // TODO : Display the formAddInventory <form> in a JQuery UI Dialog.
}

ScottsJewels.InventoryManagement.addNewInventoryClick = function () {
   // TODO : Retreive data from formAddInventory <form>, invoke addNewInventory(), and hide JQuery UI Dialog.
}

ScottsJewels.InventoryManagement.addNewInventory = function ( formData ) {
   // TODO : Make AJAX POST to Web Server with <form> data.
}
InventoryManagement.js (Stubbed Out using Namespace)

Now instead of invoking our Production Scripts as a global function like this …

addNewInventory ();

We can invoke them through their namespace like this …

ScottsJewels.InventoryManagement.addNewInventory ();

Module Pattern

Next, let’s restrict access to the functions using the Module Pattern. The Module Pattern permits encapsulation of our functions …

var ScottsJewels = ScottsJewels || {};
ScottsJewels.InventoryManagement = ScottsJewels.InventoryManagement || {};

ScottsJewels.InventoryManagement = (function () {

   return {
      selectInventoryOnChange : function (event) {

         // TODO : Get Category from selectInventoryCategory <select> and invoke getInventoryFromServer().
      },
      getInventoryFromServer : function ( inventoryCategory ) {
         // TODO : Make AJAX GET Request to Web Server for Category and invoke refreshInventoryTable() with retreived Data.
      },
      refreshInventoryTable : function ( jsonInventoryData ) {
         // TODO : Refresh <table> with Inventory Data.
      },
      showAddInventoryForm : function () {
         // TODO : Display the formAddInventory <form> in a JQuery UI Dialog.
      },
      addNewInventoryClick : function () {
         // TODO : Retreive data from formAddInventory <form>, invoke addNewInventory(), and hide JQuery UI Dialog.
      },
      addNewInventory : function ( formData ) {
         // TODO : Make AJAX POST to Web Server with <form> data.
      }
   };
});
InventoryManagement.js (Stubbed Out using Module Pattern)

Notice that all of the above functions are publically accessible in this Module …

var module = ScottsJewels.InventoryManagement();

module.addNewInventory();

The Module pattern also allows for private functions and data members …

var ScottsJewels = ScottsJewels || {};
ScottsJewels.MyModule = ScottsJewels.MyModule || {};

ScottsJewels.MyModule = (function () {

   var myPrivateDataMember = "";

   function myPrivateFunction () {
      alert(myPrivateDataMember);
   }

   return {
      myPublicFunction : function ( message ) {

         myPrivateDataMember = message;
         myPrivateFunction();
      }
   }
});

You can do this …

var module = new ScottsJewels.MyModule();

module.myPublicFunction(“Hello World”); // Alerts “Hello”.

But not this …

var module = new ScottsJewels.MyModule();

module.myPrivateFunction();

Singleton

Finally, let’s make the Module a Singleton. The Singleton Pattern provides a common context for all Production Script invocations as well as a smaller memory footprint. It’s as easy as adding () onto the end of the Module declaration. Essentially you are invoking the Module once when it is parsed and loaded …

var ScottsJewels = ScottsJewels || {};
ScottsJewels.InventoryManagement = ScottsJewels.InventoryManagement || {};

ScottsJewels.InventoryManagement = (function () {
   return {
      selectInventoryOnChange : function (event) {
         // TODO : Get Category from selectInventoryCategory <select> and invoke getInventoryFromServer().
      },
      getInventoryFromServer : function ( inventoryCategory ) {
         // TODO : Make AJAX GET Request to Web Server for Category and invoke refreshInventoryTable() with retreived Data.
      },
      refreshInventoryTable : function ( jsonInventoryData ) {
         // TODO : Refresh <table> with Inventory Data.
      },
      showAddInventoryForm : function () {
         // TODO : Display the formAddInventory <form> in a JQuery UI Dialog.
      },
      addNewInventoryClick : function () {
         // TODO : Retreive data from formAddInventory <form>, invoke addNewInventory(), and hide JQuery UI Dialog.
      },
      addNewInventory : function ( formData ) {
         // TODO : Make AJAX POST to Web Server with <form> data.
      }
   };
})();
InventoryManagement.js (Stubbed Out using Singleton Pattern)

Now you can access the singleton Module now without having to instantiate it …

ScottsJewels.InventoryManagement.addNewInventory();

Create the Test Runner

To allow our Production Scripts to interact with the HTML Markup the HTML Markup needs to be added to the Test Runner’s <div id=”qunit-fixture”> element. Here is the Test Runner with the Production Scripts and HTML Markup added. A reference to the Unit Tests exists as well (although we still need to implement them) …

<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8" />
   <title>QUnit Test Suite</title>

   <!-- QUnit Dependencies -->
   <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.5.0.css">
   <script src="http://code.jquery.com/qunit/qunit-1.5.0.js" type="text/javascript"></script>

   <!-- JQuery -->
   <script src="http://code.jquery.com/jquery-1.5.min.js" type="text/javascript"></script>
   <script src="http://code.jquery.com/ui/1.8.13/jquery-ui.min.js" type="text/javascript"></script>

   <!-- Your Production Script(s) -->
   <script src="InventoryManagement.js"></script>

   <!-- Your Unit Test Script(s) --->
   <script src="InventoryManagement.Test.js"></script>

</head>
<body>
   <div id="qunit"></div>
   <div id="qunit-fixture">
      <!-- The HTML Markup that your Production Script(s) Target -->
      <div class="manage-inventory">
         <select id="selectInventoryCategory">
            <option value="" selected="selected">(Select a Category)</option>
            <option value="Drinks">Drinks</option>
            <option value="Food">Food</option>
            <option value="Dessert">Desssrt</option>
         </select>
         <table id="tableInventory">
            <thead>
               <tr>
                  <th>SKU</th>
                  <th>Name</th>
                  <th>Quantity</th>
               </tr>
            </thead>
            <tbody/>
               <tr>
                  <td colspan="3" >No Inventory</td>
               </tr>
            </tbody>
         </table>

         <button type="button">Add Inventory</button>

         <form id="formAddInventory" class="hidden" action="http://localhost/Merchandise/UpdateMerchandise" method="post">
            <fieldset>
               <input type="text" id="inputSKU" name="sku">
               <label for="inputSKU">SKU</label>
               <input type="text" id="inputName" name="name">
               <label for="inputName">Name</label>
               <input type="text" id="inputQuantity" name="quantity">
               <label for="inputQuantity">Quantity</label>
            </fieldset>
         </form>
      </div>
   </div>
</body>
TestRunner.html

As always there are a couple of “gotchas” that you need to look out for. Specifically …

HTML Markup Synchronization

In Production HTML Markup is served by a Web Server. A Unit Test must operate in isolation of the Web Server and all HTML Markup must be hard-coded in the Test Runner. The downside of this is that you must be vigilant to keep the HTML Markup in your Test Runner synchronized with whatever is generated by your Web Server.

Singleton Module and QUnit Problems

A Singleton Javascript Module and the QUnit Test Runner’s <div id=”qunit-fixture”> are not entirely compatible. If your Module selects elements from the DOM during initialization the elements will point to stale references after the first Unit Test is run.

For example, given the following HTML Markup …

<!DOCTYPE html>
<html>
<head/>
<body>
<div/>
</body>
</html>

Production Script …

var MyModule = (function () {
   var $myDiv = null;

   $(document).ready(function () {
      $myDiv = $("div");
   });

   return {
      writeToDiv : function (message) {
         $myDiv.html(message)
      }
   }

})();

and Unit Tests …

QUnit.module( "Planets Test",
{
   setup     : function() {},
   teardown  : function() {}
});

QUnit.test( "Hello Earth", 1, function() {
   MyModule.writeToDiv("Hello Earth");
   equal( "Hello Earth", $("div").html() );
});

QUnit.test( "Hello Mars", 1, function() {
   MyModule.writeToDiv("Hello Mars");
   equal( "Hello Mars", $("div").html() );
});

The 2nd Unit Test will fail because the Test Runner refreshes the HTML Markup in <div id=”qunit-fixture”> after every Unit Test. This will cause $myDiv to point at a stale DOM element.

Create Unit Tests

Next we’re going to create the Unit Tests. To keep it simple we are just going to write a single Unit Test for each Production Script function. Each Unit Test will invoke it’s corresponding Production Script function and assert the results.

Here are the stubbed out Unit Tests along with what they need to assert …

QUnit.module( "ScottsJewels.InventoryManagement",
{
   setup     : function() {},
   teardown  : function() {}
});

QUnit.test( "selectInventoryOnChange()", 0, function() {
   // Test : Verify that getInventoryaFromServer() is called with the selected inventory as a parameter.
});

QUnit.test( "getInventoryFromServer()", 0, function() {
   // Test : Verify the AJAX HTTP GET Request is correct.
   // Test : Verify that refreshInventoryTable() is called.
});

QUnit.test( "refreshInventoryTable()", 0, function() {
   // Test : Verify the <table> is correctly refreshed in DOM.
});

QUnit.test( "showAddInventoryForm()", 0, function() {
   // Test : Verify that the appropriate <form> is displayed.
   // Test : Verify that the <form> fields are empty.
});

QUnit.test( "addNewInventoryClick()", 0, function() {
   // Test : Verify that the <form> is hidden.
   // Test : Verify that addNewInventory() is called with the appropriate data from the <form>.
});

QUnit.test( "addNewInventory()", 0, function() {
   // Test : Verify the AJAX HTTP POST Request is correct.
});
InventoryManagement.UnitTest.js (Stubbed Out)

Let’s take a look at the first couple of Unit Tests …

The Unit Test for selectInventoryOnChange() must verify that getInventoryFromServer() is invoked properly.

The Unit Test for getInventoryFromServer() needs to verify that JQuery ajax() was invoked and that it, in turn, invokes refreshInventoryTable().

Fleshing out these two Unit Tests poses a couple of problems …

  1. How does my Unit Test I verify that a Production Script function invokes another function?
  2. How do I verify that JQuery ajax() was invoked correctly?

The same solution can be applied to all three : Mocking.

Mocking Dependenices

Mocking is when you create a new object that mimics the behavior of another object. A “mock” object is oftentimes substituted when the object that it is simulating is unavailable. In Unit Testing dependencies are mocked up when it is not practical to use the actual dependency.

In Javascript functions are objects and easily mockable. This comes in handly when verifying a JQuery ajax() call or asserting the invocation of a nested function. As an example, this is how you might mock up JQuery’s dialog() function for a Unit Test. It’s important to restore the original function implementation after you have conducted you tests …

var jQueryDialog_Orginal;

try {
   // Backup.
   jQueryDialog_Orginal = jQuery.fn.dialog;

   // Implement your own.
   jQuery.fn.dialog = function () {
      // QUnit assertions...
   };

   // Run test(s) that implement jQuery Dialog.
}
finally {
   // Restore.
   jQuery.fn.dialog = jQueryDialog_Orginal;
}

Keep in mind that there are (much) more sophisticated 3rd Party mocking/stubbing Javascript libraries that you can use such as SinonJs and JSMock. This is how you would use mocking to flesh out the Unit Tests for selectInventoryOnChange()

QUnit.test( "selectInventoryOnChange()", 1, function() {

   // Test : Verify that getInventoryaFromServer() is called with the selected inventory as a parameter.

   var	$event,
         $selectInventoryCategory,
         original_getInventoryFromServer;

   // Set the Inventory Category <select> with "Food" <option>.
   $selectInventoryCategory = $("#selectInventoryCategory");
   $selectInventoryCategory.val("Food");

   // Create a "change" event.
   $event = $.Event("change");
   $event.target = $selectInventoryCategory;

   try {
      // Save a reference to getInventoryFromServer() so that it can be restored after the test has completed.
      original_getInventoryFromServer = ScottsJewels.InventoryManagement.getInventoryFromServer;

      // Now, assign a new function to getInventoryFromServer().
      ScottsJewels.InventoryManagement.getInventoryFromServer = function ( inventoryCategory ) {
         // Verify that getInventoryFromServer is invoked with the "Food" <option>.
         equal( inventoryCategory, "Food" );
      }

      // Invoke selectInventoryOnChange() and send in the "change" event.
      // selectInventoryOnChange() should invoke getInventoryFromServer which was (temporarily) redefined above.
      ScottsJewels.InventoryManagement.selectInventoryOnChange ($event);
   }
   finally {
      // Restore getInventoryFromServer().
      ScottsJewels.InventoryManagement.getInventoryFromServer = original_getInventoryFromServer;
   }
});
InventoryManagement.UnitTest.js

… and getInventoryFromServer()

QUnit.test( "getInventoryFromServer()", 3, function() {

   // Test : Verify the AJAX HTTP GET Request is correct.
   // Test : Verify that refreshInventoryTable() is called.

   var original_ajax;

   try {
      // Save a reference to JQuery's ajax() so that it can be restored after the test is complete.
      original_ajax = $.ajax;

      // Now, assign a new funtion to JQuery's ajax().
      $.ajax = function (request) {

         // Verify that "Food" was supplied as the ajax "data" parameter.
         equal(request.data.inventoryCategory, "Food");

         // Verify that the ajax is a GET.
         equal(request.type, "GET");

         // Verify that refreshInventoryTable() will be invoked if ajax is successful.
         equal(request.success, ScottsJewels.InventoryManagement.refreshInventoryTable);
      }

      // Invoke JQuery's ajax(). This should invoke the (temporarily) re-defined ajax() above.
      ScottsJewels.InventoryManagement.getInventoryFromServer ( "Food" );
   }
   finally {
      // Restore JQuery's ajax().
      $.ajax = original_ajax;
   }
});
InventoryManagement.UnitTest.js

Executing the Test Runner at this time will inevitably lead to lots of failures. This is expected as our Production Scripts are merely stubbed out …

Inventory Management Test Runner Fail

Finish Production Scripts

The last thing to do is to flesh out the Production Script functions. Ironically, the Production Scripts are a lot smaller (and less complicated) than their corresponding Unit Tests. This goes to show that Unit Testing Javascript is not a trivial task …

var ScottsJewels = ScottsJewels || {};
ScottsJewels.InventoryManagement = ScottsJewels.InventoryManagement || {};

ScottsJewels.InventoryManagement = (function () {

   $(document).ready(function () {

      var $selectInventoryCategory,
          $buttonShowAddInventory;

      $selectInventoryCategory = $("#selectInventoryCategory");
      $buttonShowAddInventory = $("#buttonShowAddInventory");

      // Hook up event handlers.
      $selectInventoryCategory.bind ( "change", ScottsJewels.InventoryManagement.selectInventoryOnChange);
      $buttonShowAddInventory.bind ( "click", ScottsJewels.InventoryManagement.showAddInventoryForm);
   });

   return {

      selectInventoryOnChange : function (event) {

         // Get Category from selectInventoryCategory <select> and invoke getInventoryFromServer().
         var inventoryCategory;

         inventoryCategory = $(event.target).val();

         if (inventoryCategory !== "") {

            ScottsJewels.InventoryManagement.getInventoryFromServer(inventoryCategory);
         }
      },
      getInventoryFromServer : function ( inventoryCategory ) {

         // Make AJAX GET Request to Web Server for Category and invoke refreshInventoryTable() with retreived Data.
         $.ajax({
            type: "GET",
            url : "http://ScottsJewels/InventoryManagement/GetInventory",
            data: { inventoryCategory : inventoryCategory },
            success: ScottsJewels.InventoryManagement.refreshInventoryTable
         });
      },
      refreshInventoryTable : function ( jsonInventoryData ) {

         // Refresh <table> with Inventory Data.
         var $tableInventoryBody,
             html;

         $tableInventoryBody = $("#tableInventory tbody");
         html = "";

         $(jsonInventoryData).each( function ( index, value ) {
            html += 	"<tr>";
            html += 		"<td class='sku'>" + value.SKU + "</td>";
            html += 		"<td class='name'>" + value.Name + "</td>";
            html += 		"<td class='quanity'>" + value.Quantity + "</td>";
            html +=	"</tr>";
         });

         $tableInventoryBody
            .empty()
            .append(html);
      },
      showAddInventoryForm : function () {

         // Display the formAddInventory <form> in a JQuery UI Dialog.

         var $formAddInventory;

         $formAddInventory = $("#formAddInventory");

         $formAddInventory[0].reset();

         $formAddInventory.dialog({
            buttons: [{	text: "Ok",
            click: ScottsJewels.InventoryManagement.addNewInventoryClick
         }]});
      },
      addNewInventoryClick : function () {

         // Retreive data from formAddInventory <form>, invoke addNewInventory(), and hide JQuery UI Dialog.

         var $formAddInventory,
             formData;

         $formAddInventory = $("#formAddInventory");

         formData = $formAddInventory.serialize();

         $formAddInventory.dialog("close");

         ScottsJewels.InventoryManagement.addNewInventory( formData );
      },
      addNewInventory : function ( formData ) {

         // Make AJAX POST to Web Server with <form> data.

         $.ajax({
            type			: "POST",
            contentType	: "application/x-www-form-urlencoded",
            url 			: "http://ScottsJewels/InventoryManagement/AddInventory",
            data			: formData
            });
      }
   };
})();
InventoryManagement.js

Executing the Test Runner at this point should result in a bunch of Passing Tests. Your Test Runner should look something like this …

Inventory Management Test Runner Pass

Click here to download this example.

Conclusion

Your responsibility as a conscientous Developer doesn’t end here. You should re-run your Unit Tests and verify complicance when …

  1. Your Production Scripts interact with the DOM and the HTML structure is modified.
  2. Your Production Scripts change.
  3. A dependency that your Production Scripts use changes.

There is also a fair share of maintenance that comes along with the Unit Testing. If the above causes any of your Unit Tests to fail you might have to revise your Unit Tests to accommodate the failure. This could be as simple as updating the HTML Markup in your Test Runner or as complicated as refactoring an entire Unit Test.


#JQuery #Javascript #Programming #SoftwareDevelopment #SoftwareEngineer #DrawnAndCoded