UI5 Testing - wridgeu/wridgeu.github.io GitHub Wiki
Some examples are based on the official UI5 documentation walkthrough. Check out the following example application for a closer look.
OPA is a set of tools used to automate and validate UI (it comes with backend mocking). Tests are organized in Journesys, based on Pages to abstract the UI and make the tests more readable. It's designed to test one UI5 application (excluding backend testing).
Other automation frameworks like Selenium, Marionette or Puppeteer are framework agnostic, meaning one must rely on the generated HTML to code automation (IDs, CSS selectors...). OPA is designed by and for UI5 developers. It is capable of manipulating UI5 controls.
Quite noticable here is the Give, When, Then pattern.
-
Arrangements (Given)
Define possible initial states, e.g. the app is started, or specific data exists. For performance reasons, starting the app is usually done only in the first test case of a journey.
Given.iStartMyApp();
-
Actions (When)
Define possible events triggered by a user, e.g. entering some text, clicking a button, navigating to another page.
When.onTheWorklistPage.iPressOnMoreData();
-
Assertions (Then)
Define possible verifications, e.g. do we have the correct amount of items displayed, does a label display the right data, is a list filled. At the end of the test case, the app is destroyed again. This is typically done only once in the last test case of the journey for performance reasons.
Then.onTheWorklistPage.theTableShouldHaveAllEntries().and.iTeardownMyApp();
-
Pages
Actual page objects that implement actions & assertions per part of the UI or in other words: An OPA5 Page object is used to group and reuse actions and assertions that are related to a specific part of the screen.
Find an example implementation of a
Page
here or here.// Page scaffold (example impl.) sap.ui.define([ "sap/ui/test/Opa5" ], function(Opa5){ Opa5.createPageObjects({ onAbstractPageName: { //baseClass: ClassOfferingCommonHelpers, actions: { iExecuteAnAction: function(){ return this.waitFor(/*...*/); }, /*...*/ }, assertions: { iCheckAnAssertion: function(){ return this.waitFor(/*...*/); }, /*...*/ } } }) });
-
Journeys
A journey represents a user’s task in our app. A journey is composed of tests.
-
Each test follows the Given, When, Then pattern.
- Given: setup the initial state of the test
- When: execute actions
- Then: check assertions
Find an example implementation of a
Journey
here or here.// Journey scaffold (example impl.) sap.ui.define([ "sap/ui/test/opaQunit" ], function(opaTest){ //declare module QUnit.module("Journey name"); //test1 opaTest("Test name", function(Given, When, Then){ Given.iStartMyapp(); When.onAbstractPageName.iExecuteAnAction().and.iExecuteAnotherAction(); Then.onAbstractPageName.iCheckAnAssertion().and.iTeardownTheApp(); }); //test2 opaTest("Test name 2", function(Given, When, Then){ /*...*/ }) })
-
Here we want to ensure that qUnit
is loaded, we load the OPA as well as pages & journeys. Finally we start the Test.
Find an example implementation of a AllJourneys.js
file here or here.
sap.ui.require([
"sap/ui/test/Opa5",
/* pages */
/* journeys */
], function(Opa5){
Opa5.extendConfig({
/* Default settings */
});
//remove in CI/CD scenario
QUnit.start();
});
The MockServer is a software component that captures AJAX requests and either answers them or lets them reach the backend. Check out the official documentation regarding the MockServer here.
- It requires the metadata to know the entities and relationships exposed by the ODATA Service.
- It initializes the entity sets by generating them or loading JSON files.
_oMockServer = new MockServer({ rootUri: "/odata/EXAMPLE_SRV/" });
_oMockServer.simulate("model/metadata.xml", {
sMockdataBaseUrl: "model/"
});
_oMockServer.start();
MockServer entities can be manipulated at any time.
var aExisting = _MockServer.getEntitySetData("SomeEntitySet");
var sGuid = "0MOCKSVRV-SOME-TEST-MOCK-GUID9999";
// Add a new entity
aExisting.push({
"Guid": sGuid,
"Title": "Generated",
"__metadata": {
id: "odata/EXAMPLE_SRV/SomeItemSet(guid'" + sGuid + "')",
uri: "odata/EXAMPLE_SRV/SomeItemSet(guid'" + sGuid + "')",
type: "EXAMPLE_SRV.SomeItem"
}
});
_oMockServer.setEntitySetData("SomeItemset", aExisting);
By default, the MockServer supports a lot of ODATA operations/hooks like: $batch
, CRUD
, Query parameters: paging, filtering, sorting
, Single-Level Navigation-properties
(function imports aren't supported).
- Definition of a Hook:
- method:
GET
,POST
,PUT
... - path: a regexp matching the API URL
- response function: behaviour implementation
- method:
var aRequests = _MockServer.getRequests();
aRequests.push({
method: "POST",
path: OData.entityNames.someItemSet,
response: function(oXhr){
//Initialize some values/fields
var oBody = JSON.parse(oXhr.requestBody);
oBody[OData.entityProperties.someItem.someAttribute] = null;
oBody[OData.entityProperties.someItem.someotherAttribute] = null;
oXhr.requestBody = JSON.stringify(oBody);
return false; //Keep default processing
}
});
_oMockServer.setRequests(aRequests);
-
Given
On the given object we can call arrangement functions like iStartMyAppInAFrame to load our app in a separate iFrame for integration testing.
-
When
Contains custom actions that we can execute to get the application in a state where we can test the expected behavior.
-
Then
Contains custom assertions that check a specific constellation in the application and the teardown function that removes our iFrame again.