Search code examples
sapui5sap-web-idemockserver

SAPUI5 Mockserver always generate same random ids


I have a UI5 application that I run it with UI5 standard build-in mock server for testing.

Here is the code from mockserver.js that is generate by Web IDE automatically:

sap.ui.define([
"sap/ui/core/util/MockServer",
"sap/ui/model/json/JSONModel",
"sap/base/util/UriParameters",
"sap/base/Log"
], function (MockServer, JSONModel, UriParameters, Log) {
"use strict";

var oMockServer,
    _sAppPath = "de/cimt/test/",
    _sJsonFilesPath = _sAppPath + "localService/mockdata";

var oMockServerInterface = {

    /**
     * Initializes the mock server asynchronously.
     * You can configure the delay with the URL parameter "serverDelay".
     * The local mock data in this folder is returned instead of the real data for testing.
     * @protected
     * @param {object} [oOptionsParameter] init parameters for the mockserver
     * @returns{Promise} a promise that is resolved when the mock server has been started
     */
    init : function (oOptionsParameter) {
        var oOptions = oOptionsParameter || {};

        return new Promise(function(fnResolve, fnReject) {
            var sManifestUrl = sap.ui.require.toUrl(_sAppPath + "manifest.json"),
                oManifestModel = new JSONModel(sManifestUrl);

            oManifestModel.attachRequestCompleted(function ()  {
                var oUriParameters = new UriParameters(window.location.href),
                    // parse manifest for local metatadata URI
                    sJsonFilesUrl = sap.ui.require.toUrl(_sJsonFilesPath),
                    oMainDataSource = oManifestModel.getProperty("/sap.app/dataSources/mainService"),
                    sMetadataUrl = sap.ui.require.toUrl(_sAppPath + oMainDataSource.settings.localUri),
                    // ensure there is a trailing slash
                    sMockServerUrl = /.*\/$/.test(oMainDataSource.uri) ? oMainDataSource.uri : oMainDataSource.uri + "/";
                    // ensure the URL to be relative to the application
                    sMockServerUrl = sMockServerUrl && new URI(sMockServerUrl).absoluteTo(sap.ui.require.toUrl(_sAppPath)).toString();

                // create a mock server instance or stop the existing one to reinitialize
                if (!oMockServer) {
                    oMockServer = new MockServer({
                        rootUri: sMockServerUrl
                    });
                } else {
                    oMockServer.stop();
                }

                // configure mock server with the given options or a default delay of 0.5s
                MockServer.config({
                    autoRespond : true,
                    autoRespondAfter : (oOptions.delay || oUriParameters.get("serverDelay") || 500)
                });

                // simulate all requests using mock data
                oMockServer.simulate(sMetadataUrl, {
                    sMockdataBaseUrl : sJsonFilesUrl,
                    bGenerateMissingMockData : true
                });

                var aRequests = oMockServer.getRequests();

                // compose an error response for each request
                var fnResponse = function (iErrCode, sMessage, aRequest) {
                    aRequest.response = function(oXhr){
                        oXhr.respond(iErrCode, {"Content-Type": "text/plain;charset=utf-8"}, sMessage);
                    };
                };

                // simulate metadata errors
                if (oOptions.metadataError || oUriParameters.get("metadataError")) {
                    aRequests.forEach(function (aEntry) {
                        if (aEntry.path.toString().indexOf("$metadata") > -1) {
                            fnResponse(500, "metadata Error", aEntry);
                        }
                    });
                }

                // simulate request errors
                var sErrorParam = oOptions.errorType || oUriParameters.get("errorType"),
                    iErrorCode = sErrorParam === "badRequest" ? 400 : 500;
                if (sErrorParam) {
                    aRequests.forEach(function (aEntry) {
                        fnResponse(iErrorCode, sErrorParam, aEntry);
                    });
                }

                // custom mock behaviour may be added here

                // set requests and start the server
                oMockServer.setRequests(aRequests);
                oMockServer.start();

                Log.info("Running the app with mock data");
                fnResolve();
            });

            oManifestModel.attachRequestFailed(function () {
                var sError = "Failed to load application manifest";

                Log.error(sError);
                fnReject(new Error(sError));
            });
        });
    },

    /**
     * @public returns the mockserver of the app, should be used in integration tests
     * @returns {sap.ui.core.util.MockServer} the mockserver instance
     */
    getMockServer : function () {
        return oMockServer;
    }
};

return oMockServerInterface;
});

All the sets in my oData metadata have the ID of type integer! (Auto incremental in the real gateway server)

The funny part is that when I create a new object of a set, it will assign 9 as the id of that object which was created for the first time, and the second created object will be 416 and so on.

What is clear is that the build-in mock server uses a random generate algorithm without or with a static seed. That is the reason that it will generate all the time same chain of the ids for each set in my metadata model.

Now my question is how can I change this behavior of the UI5 mock server?

In the other word, how can I set a random number as the seed for the mock server or how can I force it to use an incremental behavior for the ids.

The problem of the default behavior of UI5 that generates 9, 416, 6671, 2631, ... as the ids, is when one of the sets already have an item with id 9! Then by creating a new item I will have two items in my list with same ids (i.e. 9)!


Solution

  • Looking at the source code of the UI5 mock server, there doesn't seem to be a public setter for the random seed.

    If you want to simulate the generation of sequential IDs, you can use MockServer#attachAfter() to change the generated value after the mock server has finished processing the request, like so:

    oMockServer.attachAfter("POST", oEvent => {
        oEvent.getParameter("oEntity").Id = generateId()
    }, "<your entity set name>")
    

    If you need full control of how the mock server responds to a request, you can also override its behavior per request.