Getting Started With QUnit Testing
Written by Ian Elliot   
Monday, 04 March 2013
Article Index
Getting Started With QUnit Testing
Testing HTML

The Assertions

From here your task is to add assertions that are most likely to catch errors in the functions within the JavaScript file that you are testing.  As well as the ok assertion that are a number of others that you need to know about:

  • ok(bool,string) asserts that its first argument is true and displays the string.
  • equal(object1,object2,string) tests weak equality i.e. using == between two objects.
  • strictEqual(object1,object2,string) tests strict equality i.e.using === to make sure that the objects are of the same type and value.
  • deepEqual(object1, object2,string) makes a deep recursive test for equality between two objects.

There is also notEqual, notStrictEqual  and notDeepEqual which are sometimes easier to read than negting an asertion. 

You do have to be careful how you use these assertions otherwise you could well trigger failures that aren't justified. If it really is ok for a function to return anything that can be treated as a value then use equal if not use strictEqual. That is

equal(1,"1","A true assertion");

and

strictEqual(1,"1", A false assertion);

The only assertion that might be a tiny bit complicated is  deepEqual.

This is used to test that two objects, including arrays and even strings, have the same properties with identical values. The meaning of this is clear where the values are primative types such as numeric values but what if the value is itself an object complete with properties? In this case the deepEqual assertion is applied to the object to make sure it is deepEqual to the target value.  Notice that this is not equality of reference - i.e. the two objects can be distinct rather just the same object referenced by two variables.

For example:

var A={"A":1};
var B=A;
deepEqual(A,B,"equal reference")
deepEqual(A,{"A":1},"equal value");

both assertions pass bu the first is an equality of reference, i.e. both A and B reference the same object and the second is equality of value because the object that A references is a different object to the object literal in the assertion.

The Global Namespace

There is one very important testing feature that is quick to describe, easy to use and will save you hours. If you are writing quality JavaScript code then no function should add to the global namespace unless it is doing something very strange. However all you have to do is to forget to write a var infront of the first use of a variable and the global namespace is polluted.

To detect that this has happened all you have to do is to select the Check for Globals box in the test page and the tests will be run again with an extra assertion automatically applied to every function that the global namespace should be the same before and after the function has been used.

For example if you change the max function to:

function myMax(a,b){
    c=a;
    return Math.max(a,b);
}

and run the tests again with check for globals on you will see the messagge:

5 Introduced global variable(s) c.

Testing HTML

There has been a missing set of elements in all our tests so far  - the HTML that the JavaScript often interacts with.

How can we test a function that modifies some HTML by interacting with the DOM?

The JavaScript we are testing probably works with some production HTML page and we don't want to pull this into the testing page. There is also the problem of side effects. If you run a test on a function and it modifies the DOM how might this effect other functions tested later? The issue of side effects and how to handle them is a big problem in general testing but for JavaScript it is the DOM - a global resource that is the big headache.

Fortunately QUnit gives us an easy solution. You can use a Div with the id qunit-fixture to contain any HTML you want to be available to test your functions. When you run the a test it can modify anything in the fixture div and when it has finished QUnit will restore the DOM to its state before the function ran.

That is the fixture div is you HTML playground where you can do anything you want safe in the knowledge that it will not effect anything that follows. Of course if you do want to keep side effects simply create the testing HTML in the web page and not in the fixture div.

Notice that the DOM is restored to its original state after the test is complete not after each assertion. For example, suppose we have the function:

function changeText(){
 document.getElementById("myElement").
                   innerHTML="Hello qUnit";
}

 then to test it we add to the testing page:

<div id="qunit">
</div>
<div id="qunit-fixture">
    <div id="myElement"></div>
</div>

Now we can add two tests:

test("MyTest1", function() {
 changeText();
 equal(document.getElementById(
    "myElement").innerHTML,"Hello qUnit");
});

test("MyTest2", function() {
 equal(document.getElementById(
    "myElement").innerHTML,"Hello qUnit");
});

The first test runs the function which modifies the DOM and the assertion checks that the DOM has indeed been modified. The first test then ends and the DOM is restored to its original state. When the second test starts the assertion it contains fails because the DOM element doens't contain "Hello qUnit" because the changes have been reset.

By grouping tests together you can construct assertions where changes propagate to later assertions and you can use separate tests to reset the DOM.

Asynchronous Tests

Sooner or later you will encounter the need to test a function that is asynchronous. In this case you need QUnit to wait for it to complete any assertions which have become asynchronous. For example suppose myAsync accepts a call back that is run when it is complete. You might write something like:

test("async",function(){
 myAsync(function(result){
  equal(result,"ok");
 })
}

This is perfectly logical in that you want to test the value returned sooner or later by myAsync to the callback but as it is things will be a little strange. The reason is that QUnit will continue on its way running assertions and tests before the call back with the equal assertion is called. To keep things in the right order we can stop QUnit and restart it after the assertions have been run in the call back. That is:

test("async",function(){ 
 stop();
 myAsync(function(result){
  equal(result,"ok"); 
  start();
 })
}

The call to stop halts the running of tests while myAsync function is working. When it finally called the callback an assertion is computed and the test engine restarted. Notice that the call to start is within the callback function.

To make things easier you can simply use asyncTest which automatically adds a stop.

asyncTest("async",1,function(){ 
 myAsync(function(result){
  equal(result,"ok"); 
  start();
 })
}

Notice that you still have to put the start into the callback function to get QUnit running tests again. The second parameter informs QUnit of the number of assertions you are testing asynchronously so that if assertions fail because they aren't run this can be flagged as a fail.

Where next

There are lots of other features of QUnit that make testing more powerful and better organized, but you now have the basics. You now know that you don't change the production code to test it. You simply create a test page which runs tests, each test consisting of a set of assertions. You also now know how to provide an HTML playground for the functions to exercise themselves in.

From here you need to look up the use of modules to group tests together and you need to find out how to add custom asertions, but these things can wait until you need to make use of them.

Testing and development should go hand-in-hand and now you have no excuse for not adding a testing page to your project.

Related Articles

QUnit Javascript Unit Tester Released

 

justjquery

Just jQuery

 

 

 

 

To be informed about new articles on I Programmer, install the I Programmer Toolbar, subscribe to the RSS feed, follow us on, Twitter, Facebook, Google+ or Linkedin,  or sign up for our weekly newsletter.

 

blog comments powered by Disqus

 

Banner


Getting Started With jQuery - Advanced Filters

When you first encounter filters they seem easy enough - just extract the results you want from the results you have. The trouble is that filters are fun and jQuery pushes the idea beyond the obvious. [ ... ]



Getting Started With jQuery - Filters

Mastering the core of jQuery is first a matter of understanding selectors and then DOM manipulation. Filters are often confused with selectors but they are quite different and serve an important purpo [ ... ]


Other Articles

 



Last Updated ( Sunday, 09 November 2014 )
 
 

   
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.