Skip to Content
Technical Articles
Author's profile photo Volker Buzek

Testing UI5 apps, part 3.1: the Mockserver

Testing UI5 apps

After the first part of the series showed setup and Unit Tests and the second part covered Integration (aka “OPA”) Tests, we’ll broaden the scope a little and visit various topics sorrounding testing UI5 apps.

Given this article turned out to be longer than expected, I’m splitting “part3”: 3.1 covers the mockserver, 3.2 the remainig aspects.

(repeat hint from Blog Part 1: the code for this blog series along with installation instructions is located at https://github.com/vobujs/openui5-sample-app-testing – yep, by the URL you can already tell that I copied the official sample ToDo UI5 app and modified it to fit the purpose of this blog series about testing.)

TOC

Mockserver

One of the most underrated und neglected parts of UI5 is the excellent integrated mockserver. In fact, I’d postulate that -in a distributed development team- UI5’s mockserver is an ideal integration means between Frontend- and Backend-development. But don’t want to get ahead of myself and save this for the end of the post 🙂

init and start

In its standard use case, the mockserver takes an OData service’s metadata.xml file and auto-magically hooks into UI5’s OData calls. So to the UI5 app, it looks as if a “real” backend provides data.

var oMockServer = new sap.ui.core.util.MockServer({ // full namespace access for better readability
    rootUri: "/my/odata/SRV" // e.g. see manifest.json: sap.app.dataSources.mainService.uri
})
oMockServer.simulate("sap/ui/demo/todo/test/metadata.xml"); // local metadata.xml
oMockServer.start();

If you were to start the mockserver in the application’s index.html, all OData-requests will then be intercepted and answered by the mockserver, causing no more network activity.

sap.ui.getCore().attachInit(function() {
    sap.ui.require([
        "sap/ui/core/ComponentContainer",
        "sap/ui/core/util/MockServer"
    ], function(ComponentContainer, MockServer) {
        var oMockServer = new MockServer({
            rootUri: "/my/odata/SRV"
        })
        oMockServer.simulate("/test/metadata.xml");
        oMockServer.start();

        new ComponentContainer({
            name: "sap.ui.demo.todo",
            settings: {
                id: "todo"
            },
            manifest: true
        }).placeAt("root");
    });
});

on-demand access

But of course you want the mockserver to be switched on or off on demand – so for demonstration purposes, I’ve equipped the demo app with optional mockserver capabilites: by inserting sap-ui-mockserver as URL GET parameter, the mockserver can be activated (and switches the entire application to using an OData-based model).

The sap.ui.core.util.MockServer is extended as sap.ui.demo.todo.test.MockServer and reads its’ configuration from manifest.json directly.
The metadata.xml is expected in /webapp/test/metadata.xml.
Additional URL parameters are implemented for conveniently creating comfortable testing options:

  • sap-ui-mockserver-debug=true enables verbose logging: for all HTTP access methods (GET, POST, PUT, DELETE, UPDATE), the request state is logged to console
  • sap-ui-mockserver-delay=<ms> instructs the mockserver to wait <ms> milliseconds before sending a response. Very useful to simulate network latency or slow backends!

Out of the box, the mockserver can generate type-matching data for Entites and Entity Sets automatically via

oMockServer.simulate(sMetadataUrl, {
    bGenerateMissingMockData: true
})

While the resulting mockdata is convenient from a quantity-perspective, having static and/or more semantically meaningful data might be more desirable.
For better quality data, check out an extended version of the mockserver over at UI5 lab that generates more semantically valid mass data.
For having static data…

static mockdata, CR(UD)

…you need to do two things:

  1. provide an additional option for the mockserver at configuration-time:
    oMockServer.simulate(sMetadataUrl, {
        sMockdataBaseUrl: "./test/mockdata", // dir holding static data
        bGenerateMissingMockData: true
    })
    
  2. provide content in a file
    • named as <entityset>.json corresponding to the entity set name from metadata.xml,
      located in the sMockdataBaseUrl directory:
    webapp/test/
    └── mockdata
       └── todos.json
    
    • in valid JSON format
    [
        {
            "guid": "67e2793d-f4d4-4d79-9fc8-f753b80728ba",
            "title": "Start this app with the mockserver",
            "completed": true
        },
        {
            "guid": "67e2793d-f4d4-4d79-9fc8-f753b80728bb",
            "title": "Learn OpenUI5",
            "completed": false
        }
    ]    
    
    

Static mockdata served by a simulated backend allows for reproducable results in Unit- and Integration Tests – but not only that, UI’s mockserver out of the box then supports all Create, Read, Update and Delete (CRUD) on the static dataset!
Go ahead, try it: fire up the app via http://localhost:8080/?sap-ui-debug=true&sap-ui-mockserver=true&sap-ui-mockserver-debug=true, create a new ToDo item and watch the browser console:

Caveat: the mockserver doesn’t support Edm.DateTime keys in OData entities! Only possible workaround is to locally refactor them to be Edm.String 🙁

OPA test hookup

In order to hook up the mockserver to the Integration Tests, a two-step approach is necessary.

  1. detect whether the OPA tests are started with or without the mockserver
    // in /webapp/test/integration/opaTests.qunit.html
    if (oUriParameters.get("sap-ui-mockserver")) {
        _global.mockserver = true;
        _global.mockserverParameters = oUriParameters;
    }
    
  2. firing up the mockserver so it suits the tests well.
    Given the OPA tests for the openUI5-demo todo are written so that the entire application is started and stopped in every test, we need to do the same with the mockserver:

    // in every webapp/test/integration/*Journey.js
    QUnit.module(<modulename>, {
    	beforeEach: function () {
    		if (_global.mockserver) {
    			this.oMockserver = MockServer.init(_global.mockserverParameters);
    		}
    	},
    	afterEach: function () {
    		if (this.oMockserver) {
    			this.oMockserver.shutdown();
    		}
    	}
    });
    

Detecting the mockserver usage on inital OPA suite startup is necessary due to some inner works of the OPA-QUnit bridge implementation: URL GET parameters are not forwarded to each *Journey.js, so detecting them in QUnit.module-beforeEach doesn’t work.

the metadata contract

Quick sum-up: UI5’s mockserver gives you

This should enable frontend development based on metadata.xml only!
The OData service design is represented in that file and can be used at development time to create UI and UX against.

Thinking is through in a broader development context and timeline: why not design the OData service first, then give metadata.xml as a “contract” to both frontend- and backend-development? “Frontend” can use UI5’s mockserver to simulate the backend, “Backend” knows what to implement based on the service design.

This allows both development streams to work in parallel rather than having backend/OData services done before developing frontend components – not only a huge time saving potential, but also enables agile adjustment of the OData design. If “Frontend” finds a cumbersome OData design, it’s easier to fix that via the metadata.xml-contract than having to go through a “Backend” development cycle.

UI5’s mockserver has many capabilites! Only a brief intro in conjunction with Integration tests boosted the post to unforseen length – that’s why part3 of the “Testing UI5 apps” blog series is split in two; next up: automated testing, code coverage, and some useful utilities!

Assigned Tags

      1 Comment
      You must be Logged on to comment or reply to a post.
      Author's profile photo Mauricio Lauffer
      Mauricio Lauffer

      Great blog! Congrats.

      I'd like to add 2 things...

       

      Loading mock data for Entities

      I've created a custom mockserver which loads meaningful data based on the metadata.xml. IMO, it's better than the generic "field name + index" and more efficient than creating JSON files manually. Have a look at the repo, you'll find more details and demos:

      OpenUI5 Smart MockServer - https://github.com/mauriciolauffer/openui5-smart-mockserver

       

      Starting mockserver before OPA tests

      I'd start mockserver before starting the OPA tests (see example below, based on your OPA initialization) and I wouldn't kill it at every new test for the cost to stop/start is too high. Moreover, you don't want to turn mockserver off during an OPA test which communicates to the backend.

      QUnit.config.autostart = false;
      sap.ui.getCore().attachInit(function() {
        sap.ui.require([
          "path/to/localService/mockserver",
          "sap/ui/demo/todo/test/integration/AllJourneys"
        ], function(mockserver) {
          mockserver.init();
          QUnit.start();
        });
      });