Technical Articles
Code coverage for your automated testing in SAPUI5 using ui5-tooling middleware
HI all.
This is a bit of an announcement blog as I finally got around to update my outdated ui5-middleware-code-coverage plugin to be able to handle es6. The old plugin used istanbul-middleware as the main package to instantiate an express server. The problem was that the repository wasn’t maintained anymore and couldn’t handle es6 code like arrow functions and async/await.
With the new release “currently” 2.0.3 this plugin now supports everything as I written a new NYC-middleware package instead
Be aware that currently it works by adding the necessary .nyc_output folder to your repository and running NYC report from that. When you generate the coverage you will get a coverage folder in your repository which will automatically delete itself when you choose to download.
Let me show you by an example:
First generate a new SAPUI5 project. I’m using the Easy-UI5 by Marius Obert to get started
Run command after doing the installation as explained on the github page
yo easy-ui5
For this example I just stick with all the proposals and let the NPM install run.
Afterwards enter the repository and install the middleware component
npm install ui5-middleware-code-coverage --save-dev
I’ll add a button to my mainview and a controller to display a dialog. Code is mostly just copied from the SAPUI5 documentation samples.
<mvc:View controllerName="com.myorg.myUI5App.controller.MainView"
displayBlock="true"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<App id="idAppControl" >
<pages>
<Page title="{i18n>title}">
<content>
<Button id="btnPress" text="Press me" press="onPress" />
</content>
</Page>
</pages>
</App>
</mvc:View>
sap.ui.define([
"com/myorg/myUI5App/controller/BaseController",
"sap/m/Dialog",
"sap/m/Button",
"sap/m/Text"
], function(Controller, Dialog, Button, Text) {
"use strict";
return Controller.extend("com.myorg.myUI5App.controller.MainView", {
/**
* Event handler for the button on the mainview using async/await
* @returns {void}
*/
onPress: async function(){
const text = await this.delayedResponse();
if (!this.oDefaultMessageDialog) {
this.oDefaultMessageDialog = new Dialog("myDialog", {
type: "Message",
title: text,
content: new Text({ text: "Test" }),
beginButton: new Button({
type: "Emphasized",
text: "OK",
press: function () {
this.oDefaultMessageDialog.close();
}.bind(this)
})
});
}
this.oDefaultMessageDialog.open();
},
/**
* Promise to return back a text after 3 seconds
* @returns {Promise} Promise with a timeout to send hello world
*/
delayedResponse: function(){
return new Promise((resolve, reject) =>
setTimeout(() => {
resolve("Hello world");
}, 3000));
}
});
});
Now let’s use UIveri5 as the testing tool. Follow the instructions on how to get it installed from the github page.
I want to show a nice little plugin created by Adrian Marten that I discovered recently called UI5-test-recorder. It has a fair bit of functionality and is open source.
You can also check it out here from UI5con 2019.
Let’s add our first test.
I created a folder in my webapp called test and in there a conf.js file containing the following:
exports.config = {
profile: 'integration',
baseUrl: 'https://localhost:8080/index.html',
};
Now I’ll use the test recorder to press my button and assert my text.
Then i can export the code
I copy this to a file called test.spec.js in the same test folder. I will then change the code slightly to handle my delay and the exact text match.
describe('test' , function () {
it('Test 0', function () {
var button = element(by.control({ id: /idAppControl--btnPress$/}));
button.click();
browser.sleep(4000);
var title = element(by.control({ id: /myDialog-title$/}));
expect(title.getText()).toBe("Hello world");
});
});
Now when we run uiveri5 in the test folder i should hopefully come back positive. (make sure to run the serve:uimodule script as well from the package.json file.
So far so good. Now let’s add the coverage to the output.
Add the following function to your test.spec.js file. This function will execute after each test run.
afterAll(() => {
browser.executeScript(`$.ajax({
type: "POST",
url: 'http://localhost:3000/coverage/client',
data: JSON.stringify(window.__coverage__),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});`);
});
Change the test script in the package.json folder to
"test": "cd uimodule/webapp/test && uiveri5 && nyc report",
And finally enable the code coverage in the yaml file
by setting the enabled to “true”
Make sure to restart your server and then run the npm test command.
We will now get a nice little summary of the code coverage.
A folder has been added to your root called .nyc_output. This is the coverage file. You can also run command
nyc report --reporter=html
nyc report --reporter=lcov
To get a html report or lcov formatted report.
If you run your testing manually. Then you can submit the code coverage to the middleware by pasting the following into your chrome debugger
$.ajax({
type: "POST",
url: 'http://localhost:3000/coverage/client',
data: JSON.stringify(window.__coverage__),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
Then visit http://localhost:3000/coverage to see the HTML report as well.
Happy testing!
Hi Jacob,
Thanks for your blog. As i understand the ui5 app is started by the "UI5 serve" command. This command uses the configuration provided by ui5.yaml.
When i run "ui5 serve" i can see that the coverage service is started and made avalable at port 3000.
However, in our project we start the webserver by launching "approuter.js". when we run our app using approuter, the coverage service is not being launched. What is the approach to achieve this?
Hi Gris,
I haven't tested out the middleware with the approuter. Does other extensions work with this setup?
Hi Jacob,
Could you please provide one example for me to use it ,Acutally for my real project ,the coverage is always zero, I did not know which point is wrong from my side ,hope you could guide me a lot .
Hi Cynthia,
Sorry I didn't see your reply before. Are you sending the resting call to submit the coverage to the collector?