Step 28: unit test with QUnit

explain

Now that we have a test folder in the application, we can start to increase the scope of the test.

In fact, each function we have added to the application so far requires a separate test case. So far, we have completely ignored this, so let's Custom formatter in step 23 Function to add a simple unit test. We will test whether the long text of the state is correct by comparing the text from the resource package.


be careful
In this tutorial, we test a simple implementation use case. If you want to learn more about QUnit testing, please check out our Test tutorial , especially Step 2: first unit test.

preview

Now you can unit test the formatter

code

You can Walkthrough - step 28 View and download all files.

Folder structure for this step

We added a new folder unit under the test folder and placed the formatter unit test in the model subfolder. The folder structure matches the application structure, and the corresponding unit tests can be easily found.

webapp/test/unit/model/formatter.js

/*global QUnit*/

sap.ui.define([
	"sap/ui/demo/walkthrough/model/formatter",
	"sap/ui/model/resource/ResourceModel"
], function (formatter, ResourceModel) {
	"use strict";

	QUnit.module("Formatting functions", {
		beforeEach: function () {
			this._oResourceModel = new ResourceModel({
				bundleUrl: sap.ui.require.toUrl("sap/ui/demo/walkthrough") + "/i18n/i18n.properties"
			});
		},
		afterEach: function () {
			this._oResourceModel.destroy();
		}
	});


	QUnit.test("Should return the translated texts", function (assert) {

		// Arrange
		// this.stub() does not support chaining and always returns the right data
		// even if a wrong or empty parameter is passed.
		var oModel = this.stub();
		oModel.withArgs("i18n").returns(this._oResourceModel);
		var oViewStub = {
			getModel: oModel
		};
		var oControllerStub = {
			getView: this.stub().returns(oViewStub)
		};

		// System under test
		var fnIsolatedFormatter = formatter.statusText.bind(oControllerStub);

		// Assert
		assert.strictEqual(fnIsolatedFormatter("A"), "New", "The long text for status A is correct");

		assert.strictEqual(fnIsolatedFormatter("B"), "In Progress", "The long text for status B is correct");

		assert.strictEqual(fnIsolatedFormatter("C"), "Done", "The long text for status C is correct");

		assert.strictEqual(fnIsolatedFormatter("Foo"), "Foo", "The long text for status Foo is correct");
	});

});

We created a new formatter under webapp/test/unit/model JS file, which implements the unit test of the custom formatter. The formatter file we want to test is loaded as a dependency. We also need a dependency on the ResourceModel because we want to check whether the translated text is correct.

The formatter file contains only one QUnit module for formatter functions. It instantiates our ResourceBundle with localized text in the beforeEach function and destroys it again in the afterEach function. These functions are called before and after each test is executed.

Next is the unit test of the formatter function.
In the implementation of the statusText function we created in step 23, we use the following queue call:
var resourceBundle = this.getView().getModel("i18n").getResourceBundle();
Access ResourceBundle.

Since we don't want to test the controller, view or model functions, we first remove the dependencies by replacing these calls with empty shells through SinonJS and its stub methods. This occurs in the Arrange section of the unit test. SinonJS injects a stub method into all objects, so we can simply call this Stub () to create a new stub for any behavior we need to simulate.

Test stubs are functions with pre programmed behavior. In addition to the methods that can be used to change stub behavior, they also support the complete SinonJS test API. If this part is a little confused, please check the official SinonJS document to test the spy or ignore it, which will become clear later.

Then bind the stub to the statusText formatter by calling JavaScript's bind function. When the function is called with the variable fnIsolatedFormatter, the this pointer is now bound to the controller stub, and we can still pass the parameters according to our own ideas. This occurs in the "system under test" section of the test.

Finally, we execute the assertion. We examine each branch of the formatter logic by calling A separate formatter function with the expected values in the data model (A, B, C, and all other values). We strictly compare the result of the formatter function with the hard coded string we expect from the resource package, and give A meaningful error message when the test fails. Here we hard code the string to identify the problem of resource package properties. If an attribute is missing, if we check according to the actual value of the resource package (empty strings on both sides), the test will still succeed.

webapp/test/unit/unitTests.qunit.html (New)

<!DOCTYPE html>
<html>
<head>
	<title>Unit tests for SAPUI5 Walkthrough</title>
	<meta charset="utf-8">

	<script
		id="sap-ui-bootstrap"
		src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
		data-sap-ui-resourceroots='{
			"sap.ui.demo.walkthrough": "../../"
		}'
		data-sap-ui-async="true">
	</script>

	<link rel="stylesheet" type="text/css" href="https://openui5.hana.ondemand.com/resources/sap/ui/thirdparty/qunit-2.css">

	<script src="https://openui5.hana.ondemand.com/resources/sap/ui/thirdparty/qunit-2.js"></script>
	<script src="https://openui5.hana.ondemand.com/resources/sap/ui/qunit/qunit-junit.js"></script>
	<script src="https://openui5.hana.ondemand.com/resources/sap/ui/qunit/qunit-coverage.js"></script>
	<script src="https://openui5.hana.ondemand.com/resources/sap/ui/thirdparty/sinon.js"></script>
	<script src="https://openui5.hana.ondemand.com/resources/sap/ui/thirdparty/sinon-qunit.js"></script>

	<script src="unitTests.qunit.js"></script>
</head>
<body>
	<div id="qunit"/>
	<div id="qunit-fixture"/>
</body>
</html>

The so-called QUnit test suite is an HTML page that triggers all QUnit tests of the application. Most of its content is to generate the layout of the result page that you can see in the preview. We won't explain these parts further, but focus on the application part.

Let's start with namespaces. Because we are now in the webapp/test/unit folder, we actually need to go up two layers to get the src folder again. You can use this namespace in your tests to load and trigger application functionality.

First, we load some basic QUnit functions through the script tag. You can also add other QUnit tests here. Then, the HTML page loads another one called unittests QUnit. JS, which we will create next. This script will execute our formatter.

webapp/test/unit/unitTests.qunit.js (New)

/* global QUnit */

QUnit.config.autostart = false;

sap.ui.getCore().attachInit(function () {
	"use strict";

	sap.ui.require([
		"sap/ui/demo/walkthrough/test/unit/model/formatter"
	], function () {
		QUnit.start();
	});
});

This script loads and executes the formatter. If we now open webapp / test / unit / unittests. Com in the browser qunit. HTML file, we should see that our test is running and verifying the formatter logic.

appointment

  • All unit tests are placed in the webapp/test/unit folder of the application.
  • Files in the test suite are in * qunit.html end.
  • unitTests. qunit. The HTML file triggers all unit tests of the application.
  • Unit tests should be written for formatter, controller logic, and other separate functions.
  • All dependencies are replaced by stubs, testing only the functionality in scope.

Relevant information

Added by matecocido on Sat, 15 Jan 2022 10:59:14 +0200