Skip to Content
Technical Articles

Extending UI5 MockSever with ServiceWorker

This blog is about taking sap.ui.core.util.MockServer one step further.

 

sap.ui.core.util.MockServer

MockServer is one of my favourite modules in SAPUI5 framework. It provides a huge help when showcasing or testing a SAP Fiori application. However, it has its limitations.

 

openui5-smart-mockserver

I use MockServer so much that I created a new library which extends it providing more meaningful mock data: openui5-smart-mockserver. Rather than having generic meaningless data in your MockServer, it gives you random fake data that makes sense. If you’re interested, I’d written a blog about it some time ago: Make Fiori testing smart again! openui5-smart-mockserver to the rescue!

 

ServiceWorker API

ServiceWorker is a javascript worker which runs in a new thread in background. It doesn’t block the main thread and doesn’t have access to the DOM tree. It acts as a network proxy listening to all network requests your application makes. It’s a powerful tool for a plethora of features such as caching and offline strategies.

 

All together now

Using MockServer was almost perfect, I had just a couple of tiny issues. I wasn’t able to infer the HTTP Headers in the requests. Also, sometimes a request silently fails with no error message nor any sort of indication that it has ever been sent.

This’ by design, as MockServer uses Sinon.js fake server to capture the requests and returns the fake responses, the requests are not logged on the browser’s network activity nor anywhere else. Go ahead, run your mockserver.html, open the Developer Tools and check the network tab, there’s nothing there for the captured requests.

So, how one could know whether a request was sent or not? What if one wants to see the HTTP Headers for a given request? Is the API Key set? Is my JWT Bearer present? Where’s the CSRF token? What about the cookies? What if you set some headers in your application (think about uploading files)?

I put some thought on it and realized I could use the same strategy I’ve been using for capturing HTTP requests in my UI5 applications (the ones live in production, not dev/test): ServiceWorker API.

 

Skip this section if you don’t want to know about the development details about the implementation.

 

With the rough idea in mind, aware of some limitations, I’ve started the tests. I knew I should leverage on MockServer as is, without changing the module, it should be a seamless integration. I couldn’t just load MockServer into a ServiceWorker script with importScript() because of all the dependencies and how they are loaded (I tried, it didn’t work). The ServiceWorker doesn’t have direct access to the application DOM, therefore, I wasn’t planning on rewrite the whole module, this would be too much (to avoid stronger words). I had a dilemma here: I need to capture the requests with ServiceWorker and generate the mock response with MockServer, but they will never reach the network layer for the reasons aforementioned. After some coursing, testing and coffees, I got the answer.

The MockServer is started to generate all mock responses. However, it’s immediately stopped to allow the requests to be sent through the network and be captured by the ServiceWorker. The ServiceWorker is registered and immediately starts listening to the network activity (fetch event). A communication channel is created to exchange messages between the application and the ServiceWorker script. All MockServer instances are registered via this channel (message event). Now, the ServiceWorker knows which requests should be captured and which ones should be left alone. When ServiceWorker identifies a request to be mocked, it sends a message to the application asking for the mock data from the MockServer. A response is created with the mock data and the correct headers. The responses could’ve been cached using Cache API to avoid asking and generating mock data every time. It’d save some milliseconds, but the data randomness would be lost, therefore I’ve decided to not use cache.

Finally, I can see everything on the network tab! Headers, cookies, response’s body, etc.

All this logic has been implemented in openui5-smart-mockserver. If you’re curious about the code, go to the GitHub repo and check it out. Feel free to test and give your feedback.

 

How to use it?

Using it is quite simple:

Install openui5-smart-mockserver as package.json dependency;

Configure a sw-smartmockserver.js;

Configure a initMockServer.js, mockserver.js and mockserver.html like you always do, just replace sap.ui.core.util.MockServer by openui5-smart-mockserver;

Your mockserver.js will be slightly different from what you have today;

Start the MockServer, but rather than using start(), you need to first register the ServiceWorker with registerServiceWorker(“path/to/my/sw-smartmockserver.js”) then start the server with another new method startWorker() which returns a Promise.

 

sw-smartmockserver.js

// Import SmartMockServer Service Worker, it's just a workaround for scope issues with ServiceWorker API
importScripts('../../src/openui5/smartmockserver/SmartMockServerServiceWorker.js');

 

initMockServer.js

sap.ui.require([
  'sap/base/Log',
  'mlauffer/demo/openui5/smartmockserver/localService/mockserver'
],
function(Log, mockserver) {
  'use strict';

  mockserver.init()
      .then(function() {
        Log.info('SmartMockServer has been initiated!');
      })
      .catch(function(err) {
        Log.error(err);
      })
      .then(function() {
        sap.ui.require(['sap/ui/core/ComponentSupport']);
      });
});

 

mockserver.js

sap.ui.define([
  'openui5/smartmockserver/SmartMockServer',
  'mlauffer/demo/openui5/smartmockserver/Component'
], function(SmartMockServer, Component) {
  'use strict';

  return {
    init: function() {
      const manifestApp = Component.getMetadata().getManifestEntry('sap.app');
      const mainDataSource = manifestApp.dataSources['NORTHWIND'];
      const metadataUrl = sap.ui.require.toUrl('mlauffer/demo/openui5/smartmockserver/') + mainDataSource.settings.localUri;
      const mockServerUrl = /.*\/$/.test(mainDataSource.uri) ? mainDataSource.uri : mainDataSource.uri + '/';
      const mockServer = new SmartMockServer({rootUri: mockServerUrl});

      SmartMockServer.config({
        autoRespond: true,
        autoRespondAfter: 1
      });

      mockServer.simulate(metadataUrl, {
        bGenerateMissingMockData: true
      });

      const myLocalServiceWorkerFilename = sap.ui.require.toUrl('mlauffer/demo/openui5/smartmockserver/') + 'sw-smartmockserver.js';
      return SmartMockServer.registerServiceWorker(myLocalServiceWorkerFilename)
          .then(function() {
            return mockServer.startServiceWorker();
          });
    }
  };
});

 

Demo time

The first demo doesn’t use the ServiceWorker feature, it’s just the SmartMockServer returning nice fake data, notice there’s no OData requests in the network tab:

https://mauriciolauffer.github.io/openui5-smart-mockserver/demo/webapp/index.html

No%20OData%20network%20requests

No OData network requests

 

The second one is using the ServiceWorker feature, now you can see the OData requests in the network tab:

https://mauriciolauffer.github.io/openui5-smart-mockserver/demo-sw/webapp/index.html

Now%20you%20see%20me

Now you see me

 

Show me the code

You can check the library and demo code in the git repo. Feel free to fork, contribute to the project and give your feedback.

https://github.com/mauriciolauffer/openui5-smart-mockserver

 

Be the first to leave a comment
You must be Logged on to comment or reply to a post.