Show TOC

Cookbook for Testing Controls with QUnitLocate this document in the navigation structure

Test Cases

You can use a factory function. To keep this pointer and have a descriptive message, you should use the test inside of the function and pass a test name to it.

Internally, we prefer to pass an object to the test for retrieving the values - it makes the test cases readable.

function renderBarInPageTestCase(sTestName, oOptions) {
    QUnit.test(sTestName, function (assert) { 
        // System under Test
        var oBar = new sap.m.Bar();

        oBar.placeAt("qunit-fixture");

        // Act
        oBar.applyTagAndContextClassFor(oOptions.context);

        sap.ui.getCore().applyChanges();

        // Assert
       assert.strictEqual(oBar.getDomRef().nodeName, oOptions.expectedTag.toUpperCase());
       assert.ok(oBar.$().hasClass(oOptions.expectedClass), "The bar has the context class: " + oOptions.expectedClass);

        // Cleanup
        oBar.destroy();
    });
};

renderBarInPageTestCase("Should render the header context", {
    context : "header",
    expectedTag : "header",
    expectedClass : "sapMHeader-CTX"
});

renderBarInPageTestCase("Should render the header context", {
    context : "subheader",
    expectedTag : "header",
    expectedClass : "sapMSubHeader-CTX"
});

renderBarInPageTestCase("Should render the header context", {
    context : "footer",
    expectedTag : "footer",
    expectedClass : "sapMFooter-CTX"
});
Testing Control Events

You cannot test for event parameters in SAPUI5 so you have to record them. Nevertheless, you can still use Sinon to retain the spy's call counting capabilities. Here is a working example for this:

QUnit.test("Should set the Hash", function(assert) { 
    //Arrange
    var aCalls = [],
        fnHashChanged = function(oEvt) {
                 aCalls.push({ newHash : oEvt.getParameter("newHash"), oldHash : oEvt.getParameter("oldHash") });
        },
        oSpy = this.spy(fnHashChanged);

    
    //System under Test
    var oHashChanger = new sap.ui.core.routing.HashChanger();
    oHashChanger.init();
    oHashChanger.attachEvent("hashChanged", oSpy);

    //Act
    oHashChanger.setHash("one", true);
    oHashChanger.setHash("two");

    //Assert
    assert.strictEqual(oSpy.callCount, 2, "did change the Hash two times");

    assert.strictEqual(aCalls[0].newHash, "one", "first event was correct"); 
    assert.strictEqual(aCalls[1].newHash, "two", "second event was correct");
    
    //Cleanup
    oHashChanger.destroy();
});
Testing User Interactions

When testing user interactions, you can use sap.ui.test.qunit to trigger events.

Here is an example for when a user presses Esc on the select:

QUnit.test("Should close the popup menu if it is open and you press escape", function(assert) {
    // Arrange
    var oContstructor = {
        items: [
        new sap.ui.core.Item({
            key: "0",
            text: "item 0"
        }),

        new sap.ui.core.Item({
            key: "1",
            text: "item 1"
        })
       ]
    };

    // System under test
    var oSelect = new sap.m.Select(oContstructor);

    oSelect.placeAt("select-content");
    sap.ui.getCore().applyChanges();

    // Arrange after rendering
    oSelect.focus();
    var fnEscapeSpy = this.spy(oSelect, "onsapescape");
    var fnCloseSpy = this.spy(oSelect, "close");

    // Act
    sap.ui.test.qunit.triggerKeydown(oSelect.getDomRef(), jQuery.sap.KeyCodes.ESCAPE);

    // Assertion
    assert.strictEqual(fnEscapeSpy.callCount, 1, "onsapescape() method was called exactly once");
    assert.strictEqual(fnCloseSpy.callCount, 0, "close() method is not called");

    // Cleanup
    oSelect.destroy();
});
Testing the Re-rendering

In this example, you will test to see whether the control fails to rerender. The control has overwritten the setter of the tooltip property to avoid triggering a re-rendering.

To test this, we add an eventDelegate to see how often the rendering function is called. We need to make sure that we apply the changes after setting the property because we want SAPUI5 to render synchronously:

QUnit.test("Should supress rerendering when tooltip is set", function(assert) { 
    // Arrange
    var oContructor = {
        tooltip : "foo"
        };
    var oRerenderingSpy = this.spy();

    // System under Test
    var oLabel = new sap.m.Label(oContructor);
    oLabel.placeAt("qunit-fixture");
    sap.ui.getCore().applyChanges();

    oLabel.addEventDelegate({
        onBeforeRendering : oRerenderingSpy
    });

    // Act
    oLabel.setTooltip("bar");
    sap.ui.getCore().applyChanges();

    // Assert
    assert.strictEqual(oRerenderingSpy.callCount, 0, "Did not rerender");
    assert.strictEqual(oLabel.getTooltip(), "bar", "Tooltip property got set");
    assert.strictEqual(oLabel.$().attr("title"), "bar", "Tooltip got updated");

    // Cleanup
    oLabel.destroy();
});
Testing with Models

When testing with models, you need to make sure that you also setup/destroy your model inside a test itself. OData tests will always be integration tests, since you will require multiple files in order to use the mock server. You may use a factory method to do this.

An example for setting up the mock server is shown below:

jQuery.sap.require("sap.ui.app.MockServer");
jQuery.sap.require("sap.ui.model.odata.ODataModel");

function startMockServer(iRespondAfter) {
    // configure respond to requests delay
    sap.ui.app.MockServer.config({
        autoRespond : true,
        autoRespondAfter : iRespondAfter || 10
    });

    // create mockserver
    var oMockServer = new sap.ui.app.MockServer({
        rootUri : "http://sap.com/service/"
    });

    // start and return
    oMockServer.simulate("data/metadata.xml", "data");
    oMockServer.start();
    return oMockServer;
}

//Your test:
QUnit.test("Should do something with the model", function (assert) {
    //Arrange
    var oMockServer = startMockServer(0),
    
    // System under Test + Act

    //Cleanup
    oMockServer.stop();
});

When using the mock server, you can use async tests since calling respond each time on the mock server does not help the readability of the test.

After setting up the mock server, we set up the model as follows:

function createODataModel(sURL, mSettings) {
    sURL = sURL || "http://sap.com/service/";
    var oModel = new sap.ui.model.odata.ODataModel(sURL, true);
    
    mSettings = mSettings || {};
    jQuery.each(mSettings, function(sProperty, vValue) {
        sProperty = jQuery.sap.charToUpperCase(sProperty);
        oModel["set" + sProperty](vValue);
    });
    
    return oModel;
}

//Your test:
QUnit.test("Should do something with the model", function(assert) {
    // Arrange
    var oModel = createODataModel(),
    oMockServer = startMockServer(0),
    done = assert.async();

    // System under Test + Act + call done();

    // Cleanup
    oModel.destroy();
    oMockServer.stop();
});

You can now bind your model against your control and test whatever you want.

We use clock.tick to trigger the server response. If you didn't do this, the text of the label would still be empty:

//Your test:
QUnit.test("Should do something with the model", function(assert) {
    // Arrange
    var oModel = createODataModel(),
        oMockServer = startMockServer(50);

    // System under Test
    var oLabel = new sap.m.Label({
        text : "{/myProperty}"
    });

    oLabel.placeAt("qunit-fixture");
    sap.ui.getCore().applyChanges();

    // Act - trigger the request
    this.clock.tick(50);

    // Assert
    assert.strictEqual("myExpected", oLabel.getText(), "The expected text was present");

    // Cleanup
    oModel.destroy();
    oMockServer.stop();
});