Show TOC

QUnit Testing FundamentalsLocate this document in the navigation structure

QUnit is a powerful, easy-to-use JavaScript unit testing framework. It is used by the jQuery, jQuery UI and jQuery Mobile projects and is capable of testing any generic JavaScript code. It supports asynchronous tests out-of-the-box.

The following section gives you some background information about QUnit tests in general, explains how to set up your environment for QUnit testing, and how to write and execute QUnit tests.

Note Before you begin setting up a QUnit test environment, we suggest that you read the background information and introduction to the QUnit test API itself, which is available on the external web site http://api.qunitjs.com/. This official QUnit documentation features a complete description of the QUnit test API and contains many examples. For detailed information about creating QUnit tests for SAPUI5 controls, see QUnit Testing for SAPUI5 Controls.
Why Does SAPUI5 Use QUnit Tests?

QUnit tests provide good support for asynchronous testing. These types of tests are often needed for UI functional tests, for example if you have to wait until rendering is done, animations are complete, or a backend call returns. In addition, a QUnit test page can be executed standalone in the browser without the need of an additional "tool". This makes the creation and execution of single QUnit tests much easier. Finally, QUnit is closely related to jQuery, which is also a fundamental part of SAPUI5.

Creating a QUnit Test Page

As a prerequisite for creating a test, you need to have created a SAPUI5 application (such as myapp). Once you have done this, continue with the steps described below:

Create a QUnit test page myqunittest.qunit.html in the folder test-resources/.
Note The file name XYZ.qunit.html is a recommendation to clearly indicate that this is a QUnit test. Technically, the .qunit name extension is not required.
You can use the file template shown below. This code snippet shows a basic QUnit test template which is used for SAPUI5 control tests, for example:
<html>
  <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
       
    <base href="../../../../../../">
    <!--[if lte IE 9]><script>
         (function() {
                var baseTag = document.getElementsByTagName('base')[0];
                baseTag.href = baseTag.href;
         })();
    </script><![endif]-->
   
    <script id="sap-ui-bootstrap" 
        src="resources/sap-ui-core.js"
        data-sap-ui-theme="sap_bluecrystal"
        data-sap-ui-noConflict="true">
    </script>
   
    <link rel="stylesheet" href="resources/sap/ui/thirdparty/qunit.css" type="text/css" media="screen" />
    <script type="text/javascript" src="resources/sap/ui/thirdparty/qunit.js"></script>
    <script type="text/javascript" src="resources/sap/ui/qunit/qunit-junit.js"></script>
    <script type="text/javascript" src="resources/sap/ui/qunit/QUnitUtils.js"></script>

    <script language="javascript">
    /* Create and structure your QUnit tests here
    Documentation can be found at http://docs.jquery.com/QUnit
    
    Example:
      module("Module A");
      test("1. a basic test example", 2, function() {
        ok( true, "this test is fine" );
        var value = "hello1";
        equals( value, "hello1", "We expect value to be 'hello1'" );
      });
    */
    </script>
  </head>
  <body>
    <div id="qunit"></div>
    <!-- [TODO: add additional html content here] -->
  </body>
</html>

You can see above that a QUnit test page includes the SAPUI5 bootstrap (sap-ui-core.js), the QUnit related files (qunit.css and qunit.js) as well as the SAPUI5 utilities (QUnitUtils.js). The html body contains the tags needed by QUnit to report the test results. Each UI5 library also contains a QUnit test template that you can copy. You can find it under ../src/test/uilib/<library-name>/qunit/template.qunit.html.

Writing Test Functions

This section gives a short introduction to how test functions can be written. For a complete reference of what can be done using QUnit testing, refer to the documentation section mentioned above.

Write your test code (like the following example) into the template introduced in the previous section:

    /* Create e.g. a SAPUI5 control which you need for your tests
       Alternatively you can do this also in the setup phase of a module */
    var oButton = new sap.ui.commons.Button("myButton", {text: "Click me"});
    //...
    oButton.placeAt("myContent");


    /* The QUnit processing starts automatically when the page is
       loaded. If you want to delay the start because of some
       additional preparation work you can use the following utility
       function: */
    qutils.delayTestStart(5000);

    /* The module call can be used to categorize your test functions.
       In addition it is possible to define actions which are processed
       during setup and tearDown. */
    module("Module A");
    
    /* Example for a non-asynchronous test function:
       The first parameter is the name of the test,
       the second (optional) parameter is the number of expected assertions in the test,
       the third parameter is the test function to call when the tests runs. */
    test("Test 1", 3, function() {
        ok( true, "this test is fine" );
        var value = "hello1";
        equals( value, "hello1", "We expect value to be 'hello1'" );

        /* You can also do some actions between the assertions,
           like triggering a keydown event with Enter key on the
           Dom element with ID 'myButton' using the utilities.
           Note: The utility function simulates a keyboard event
                 using 'jQuery.trigger'. This is not a 'real'
                 event which comes from the browser and there might
                 be differences you must be aware of: When the
                 user presses the Enter key on a button several
                 events are fired by the browser like keydown, keyup,
                 click, .... The function below ONLY simulates a
                 keydown! */
        sap.ui.test.qunit.triggerKeydown("myButton", "ENTER");

        ok( true, "another test after the action" );
    });

    /* Modules have a second, optional "lifecycle" parameter. The life cycle object can 
       have two methods "setup" and "teardown". Both methods are called for each test
       of the module. It is best practice to use those life cycle methods to have standelone
       tests that do not have dependencies on other tests. */
    module("Module B", {
      setup : function(){
       // Code needed for the tests of this module
       // this.foo = new Bar();
      },

      teardown : function(){
       // Cleanup here
       // this.foo = null;
      }
    });
    

    /* Example for an asynchronous test function:
       The first parameter is the name of the test,
       the second (optional) parameter is the number of expected assertions in the test,
       the third parameter is the test function to call when the tests runs. */
    asyncTest("Test 2", 3, function() {
        
        /* Instead of using the second parameter in the test definition you can
           define the number expected assertions in the function body.
           This is handy, when you write tests with different outcome. */
        // expect(3);

        /* First you start with tests in the normal flow */
        ok( true, "this test is fine" );

        /* Asynchronous tests are automatically stopped. There is
           no need to call "stop" explicitly  */

        setTimeout(function(){
           ok( true, "this test is executed asynchronously" );
           /* Do the asynchrounos tests and give QUnit the sign
              to go on with the next test function via 'start'
              when the processing of the current one is completed */
           start();
        }, 1000);

        /* Do the things which needs a test delay, e.g. press
           a button which starts a backend call */
        sap.ui.test.qunit.triggerKeydown("myButton", "ENTER");

        ok( true, "this test is not executed asynchronously" );
    });

For the test example shown above, the following additional line was added to the body of the test template:

<div id="myContent"></div>
Executing a Test

Standalone Unit Testing

When you have created a QUnit test page as described in the previous section, you can easily run this test page without the need of any tool in any browser by using the URL of the QUnit test page (e.g. http://localhost:8080/myapp/test-resources/myqunittest.qunit.html). This will execute the test and then inform you about its success or show you any errors.

Sinon.JS: Spies, Stubs, Mocks, Faked Timers & XHR

By integrating Sinon.JS for QUnit, you can use spies, stubs, mocks, faked timers or faked XHR. For more information about using sinon.js, see the official documentation at http://sinonjs.org/docs/. Essentially, all you have to do is add sinon.js to the script section of your QUnit test HTML file (add the files after the qunit.js file), as shown below:

  <script type="text/javascript" src="../../../../../resources/sap/ui/thirdparty/qunit.js"></script>

  <!-- SINON JS SUPPORT START -->

  <script type="text/javascript" src="../../../../../resources/sap/ui/thirdparty/sinon.js"></script>
  <!--[if IE]>
  <script type="text/javascript" src="../../../../../resources/sap/ui/thirdparty/sinon.ie.js"></script>
  <![endif]-->
  <script type="text/javascript" src="../../../../../resources/sap/ui/thirdparty/sinon.qunit.js"></script>

  <!-- SINON JS SUPPORT END -->

  <script type="text/javascript" src="/jsunit-testrunner/qunit/qunit-jsunit.js"></script>
  <script type="text/javascript" src="../../../../../resources/sap/ui/qunit/QUnitUtils.js"></script>

The following examples show you the basic way in which Sinon.JS can be used. These examples are adapted from the official Sinon.JS documentation available at http://sinonjs.org/docs/:

A simple spy test:

test("Spy", 2, function() {

  var callback = sinon.spy();
  var oButton = new sap.ui.commons.Button();
  oButton.attachPress(callback);
  ok(!callback.called, "Callback Spy not called yet");
  oButton.firePress();
  ok(callback.called, "Callback Spy called");
  oButton.destroy();
});

A simple stub test:

test("Stub", 1, function() {
  sinon.stub(jQuery, "ajax").yieldsTo("success", [1, 2, 3]);

  jQuery.ajax({
    success: function (data) {
    deepEqual(data, [1, 2, 3], "Right data set");
    }
  });
  jQuery.ajax.restore();
});

A simple mock test:

test("Mock", 2, function() {
  var myAPI = { method: function () {} };

  var mock = sinon.mock(myAPI);
  mock.expects("method").once().throws();

  try {
    myAPI.method(); 
  } catch (exc) {
    ok(mock.verify(), "Mock function called and all expectations are fullfilled");
  }
  mock.restore();
});

A simple faked timer test:

test("Basic", 1, function() {
  var oClock = sinon.useFakeTimers();
  setTimeout(function() {
    ok(true, "Called without need of async test");
  },800);
  oClock.tick(800);
  oClock.restore();
});

A simple faked XHR test:

module("Faked XHR", {
  setup : function() {
    this.xhr = sinon.useFakeXMLHttpRequest();
    var requests = this.requests = [];

    this.xhr.onCreate = function (xhr) {
    requests.push(xhr);
    };
  },
                
  teardown : function() {
    this.xhr.restore();
  }
});
            
test("Basic", 2, function() {
  var callback = sinon.spy();
                
  jQuery.ajax("test", {
    success : callback
  });
                
  equal(1, this.requests.length, "Right number of requests");

  this.requests[0].respond(200, { "Content-Type": "application/json" },
                                         '[{ "foo": "bar", "bar" : "foo" }]');
  ok(callback.calledWith([{ "foo": "bar", "bar" : "foo" }]), "Data is called right");
});
QUnit Version Information

This section describes the differences in behavior between QUnit versions prior to 1.0 and QUnit versions after 1.0. See the table below for further details:

Table 1: QUnit Version
Prior to QUnit 1.0 As of QUnit 1.0 What Has Changed Exactly?
stop(timeout) stop(semaphore) In QUnit versions prior to 1.0 stop pauses the execution for a certain time when called with the optional timeout argument. In the new QUnit version, stop is called with the number of start calls needed to finish the test.
equals equal equals is deprecated. Please rename all equals references in your code to equal.
raises throws raises is deprecated. Please rename all raises references in your code to throws.
same deepEqual same is deprecated. Please rename all same references in your code to deepEqual.