npm install --global grunt-cli
cd $your_git_clone_dir
npm install
grunt serve
(open browser and point to
http://localhost:8080/test/unit/unitTests.qunit.html)
webapp
<snip />
βββ test
β βββ integration
β β βββ AllJourneys.js
β β βββ FilterJourney.js
β β βββ SearchJourney.js
β β βββ TodoListJourney.js
β β βββ opaTests.qunit.html
β β βββ pages
β β βββ App.js
β β βββ Common.js
β βββ testsuite.qunit.html
β βββ unit
β βββ allTests.js
β βββ controller
β β βββ App.controller.js
β βββ unitTests.qunit.html
<snip />
<!DOCTYPE html>
<html>
<head>
<title>Unit tests for Todo List</title>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta charset="utf-8">
<script id="sap-ui-bootstrap"
src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-theme="sap_belize"
data-sap-ui-bindingSyntax="complex"
data-sap-ui-compatVersion="edge"
data-sap-ui-preload="async"
data-sap-ui-resourceRoots='{"sap.ui.demo.todo": "../../"}'>
</script>
<!--<script src=".https://sapui5.hana.ondemand.com/resources/sap/ui/thirdparty/qunit.js"></script>-->
<!--<script src="https://sapui5.hana.ondemand.com/resources/sap/ui/qunit/qunit-css.js"></script>-->
<!-- use QUnit v2 instead of v1 - v1 is above -->
<script src="https://sapui5.hana.ondemand.com/resources/sap/ui/thirdparty/qunit-2.js"></script>
<script src="https://sapui5.hana.ondemand.com/resources/sap/ui/qunit/qunit-2-css.js"></script>
<script>
QUnit.config.autostart = false;
sap.ui.getCore().attachInit(function() {
sap.ui.require([
"sap/ui/demo/todo/test/unit/allTests"
], function() {
QUnit.start();
});
});
</script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
</body>
</html>
html
file just as index.html
(for starting your standalone UI5 app), but by convention named unitTests.qunit.html
and located at $your_project/webapp/test/unit/
.<script id="sap-ui-bootstrap" ...></script>
along with starting the application after UI5's core has "booted" up (sap.ui.getCore().attachInit(...)
). (You did know about the strict asynchronous coding approach you should follow with UI5 so you're prepared for the UI5 evolution to come, right? Right?!?).<script src=".../qunit-2..."></script>
) along with starting QUnit itself (QUnit.start()
). Note that QUnit 2 is referenced instead of QUnit 1. Why? We'll get to that in part three of the series, but as a brief glimpse ahead: version 2 has some essential capabilities such asQUnit.only()
instead of always executing the entire modulebefore
and after
) instead of having this for each test only (beforeEach
, afterEach
)data-sap-ui-resourceRoots='{"sap.ui.demo.todo": "../../"}
tells the QUnit-UI5-environment to look for the application files in $your_project/webapp
(given that the unitTests.qunit.html
is located at $your_project/webapp/test/unit/
).$your_project/webapp/test/unit/allTests.js
that is required()
in the above attachInit callback
.webapp/test/unit/
βββ allTests.js
βββ controller
β βββ App.controller.js
βββ unitTests.qunit.html
unitTests.qunit.html
, the two <div>
s in body
fit a special purpose:qunit
is where the HTML output of the Unit Tests will be injected,qunit-fixture
is intended as the container for DOM-relevant runtime operations. More on that later in part four of the blog series.$your_project/webapp/test/unit/controller/App.controller.js
contains the Unit Tests pertaining to $your_project/webapp/controller/App.controller.js
- the file name resemblance is by convention only.sap.ui.define([
"sap/ui/demo/todo/controller/App.controller"
], function(AppController) {
"use strict";
QUnit.module("test group");
QUnit.test("verify value and type", function(assert) {
// implementation
var vSomeVar = "42";
assert.strictEqual(42, vSomeVar, "42 and " + vSomeVar + " have the same value and type");
// assert will fail
});
// pidgin code below
QUnit.module("...");
QUnit.test("...");
QUnit.test("...");
QUnit.test("...");
QUnit.module("...");
QUnit.test("...");
QUnit.test("...");
//...
});
sap.ui.define
syntax is the same as in application coding, providing an asynchronous module loading mechanism. A controller file is loaded and referenced at runtime via AppController
.QUnit.module
"groups" all subsequent tests under the given name.QUnit.test
declares the actual Unit Test - with the variable assert
being at the center of it. assert
is intended for the final check of a condition that has previously been induced.assert
has a descriptive API for testing conditions.assert.strictEqual
will fail: the integer 42 and the string "42" are not of the same type.assert.equal(42, vSomeVar, "42 and " + vSomeVar + " have the same value");
assert
-examples. How to reference the UI5 application coding for testing is illustrated in the next section.onInit()
:sap.ui.define([
"sap/ui/demo/todo/controller/App.controller"
], function(AppController) {
"use strict";
QUnit.test("standalone controller method w/o dependencies", function(assert) {
// arrangement
var oController = new AppController();
// action
oController.onInit();
// assertions
assert.ok(oController.aSearchFilters);
assert.ok(oController.aTabFilters);
});
});
// pidgin controller code
method: function() {
var oControl = this.getView().byId("someControlId");
var $control = oControl.getDomRef();
jQuery($control).animate(...);
}
sap.ui.core.mvc.Controller.getView()
or sap.ui.core.Element.getDomRef()
.sinon.stub()
, methods can be "overwritten" with specific code instructions:// pidgin code
sinon.stub(Module, methodName).returns(customCode);
sap.ui.define([
"sap/ui/base/ManagedObject",
"sap/ui/core/mvc/Controller",
"sap/ui/demo/todo/controller/App.controller",
"sap/ui/model/json/JSONModel",
"sap/ui/thirdparty/sinon",
"sap/ui/thirdparty/sinon-qunit"
], function(ManagedObject, Controller, AppController, JSONModel/*, sinon, sinonQunit*/) {
"use strict";
QUnit.test("controller method that uses getView and getDomRef", function (assert) {
//// begin arrangements
// regular init of controller
var oController = new AppController();
// regular init of a JSON model
var oJsonModelStub = new JSONModel({});
// construct a dummy DOM element
var oDomElementStub = document.createElement("div");
// construct a dummy View
var oViewStub = new ManagedObject({});
// mock View.byId().getDomRef()
oViewStub.byId = function(sNeverUsed) {
return {
getDomRef : function() {
return oDomElementStub;
}
}
};
// regular setting of a model to a View
oViewStub.setModel(oJsonModelStub);
// stubbing Controller.getView() to return our dummy view object
var oGetViewStub = sinon.stub(Controller.prototype, "getView").returns(oViewStub);
//// end arrangements
// prepare data model for controller method
oJsonModelStub.setProperty("/newTodo", "some new item");
// actual test call!
oController.addTodo();
// check result of test call
assert.strictEqual(oJsonModelStub.getProperty("/todos").length, 1, "1 new todo item was added");
// follow-up: never forget to un-stub aka
// restore the original behavior, here: Controller.prototype.getView()
oGetViewStub.restore();
});
});
Promise
object)./**
* demonstration purpose only: retrieve todos from JSON model
* async-Promise-style
*
* @return {Promise}
*/
getTodosViaPromise: function () {
return new Promise(function (fnResolve, fnReject) {
var oModel = this.getView().getModel();
if (!oModel) {
fnReject("couldn't load the application model")
} else {
fnResolve(oModel.getProperty("/todos"));
}
}.bind(this))
},
assert.async()
to let the QUnit framework know to expect an asynchronous test.sap.ui.define([
"sap/ui/base/ManagedObject",
"sap/ui/core/mvc/Controller",
"sap/ui/demo/todo/controller/App.controller",
"sap/ui/model/json/JSONModel",
"sap/ui/thirdparty/sinon",
"sap/ui/thirdparty/sinon-qunit"
], function (ManagedObject, Controller, AppController, JSONModel/*, sinon, sinonQunit*/) {
"use strict";
QUnit.test("async function in controller", function (assert) {
// tell QUnit to wait for it
var fnDone = assert.async();
// arrangements
// regular init of controller
var oController = new AppController();
// regular init of a JSON model
var oJsonModelStub = new JSONModel({
"todos": []
});
// construct a dummy View
var oViewStub = new ManagedObject({});
// regular setting of a model to a View
oViewStub.setModel(oJsonModelStub);
// stubbing Controller.getView() to return our dummy view object
var oGetViewStub = sinon.stub(Controller.prototype, "getView").returns(oViewStub);
// action + assertion: start the Promise chain!
oController.getTodosViaPromise()
.then(function (aTodos) {
assert.ok(aTodos.length >= 0, "todos exist (zero or more)");
})
.then(oGetViewStub.restore) // follow-up: never forget to un-stub!
.then(fnDone) // tell QUnit test is finished
// never forget to catch potential errors in the promise chain
// and do proper clean up
.catch(function (oError) {
assert.ok(false, "Error occured: " + oError);
// follow-up: never forget to un-stub!
oGetViewStub.restore();
// tell QUnit test is finished
fnDone();
});
});
});
unitTests.qunit.html
was used for bootstrapping QUnit and the application under test for executing Unit Tests.You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
11 | |
9 | |
7 | |
6 | |
4 | |
4 | |
3 | |
3 | |
3 | |
3 |