Mocking RESTMessageV2.execute in ATF Jasmine test causes JavaException

ab7289
Tera Contributor

I'm working on creating ATF tests to unit test some script includes in a scoped application. In the script include WebServiceUtil, I have a function that handles configuring and sending a RESTMessageV2 request.

 

The function being tested is as follows:

 

_rest: function(
        restMessage,
        restMethod,
        attrs = {},
        attrsNoEscape = {},
	rest
    ) {
		if (this.debug) {
            gs.info("Calling {0} {1}\nattrs: {2}\nattrsNoEscape:{3}",
                restMessage,
                restMethod,
                (!gs.nil(attrs) ? this.json.encode(attrs) : "{}"),
                (!gs.nil(attrsNoEscape) ? this.json.encode(attrsNoEscape) : "{}"));
        }

        if (this.debug) {
            var msg = "Calling :" +
                "\nrestMessage: " + restMessage +
                "\nrestMethod: " + restMethod +
                "\nParams: ";

            Object.keys(attrs).forEach(function(key) {
                msg += "\nkey: " + key + ", value: " + attrs[key];
            });
            msg += "\nParams No Escape: ";
            Object.keys(attrsNoEscape).forEach(function(key) {
                msg += "\nkey: " + key + ", value: " + attrsNoEscape[key];
            });
            gs.info(msg);
        }

        var requestBody,
            responseBody,
            response,
            status,
            jObj;

        try {
            // set string parameters
            Object.keys(attrs).forEach((k) =>
                !gs.nil(attrs[k]) ?
                rest.setStringParameter(k, attrs[k]) :
                null);
            // set non-escaped string parameters
            Object.keys(attrs).forEach((k) =>
                !gs.nil(attrsNoEscape[k]) ?
                rest
                .setStringParameterNoEscape(k, attrsNoEscape[k]) :
                null);

            // set the host name
            rest.setStringParameter("host",
                gs.getProperty("x_###_modtrak.host"));

            if (!gs.nil(this.mid)) {
                rest.setMIDServer(this.mid);
            }

			gs.info("pre-execute");
            response = rest.execute();
			gs.info("post-execute");
            responseBody = response.getBody();
			gs.info("post-getBody, pre-getStatusCode");
            status = response.getStatusCode();
			gs.info("got status code");
            jObj = this.json.decode(responseBody);
        } catch (ex) {
            var err = "There was an internal error when attempting to contact ";
            err += "The ModTrak REST Service: " + restMessage + ":" + restMethod;
            err += "\nError: " + ex;
            err += "\nError Exception: " + ex;
            if (!gs.nil(response)) {
                err += "\nREST Error Code: " + response.getErrorCode();
                err += "\nREST Error msg: " + response.getErrorMessage();
            }
            gs.error(err);
            responseBody = !gs.nil(ex) ? ex :
                response.getErrorMessage();
            return {
                error: true,
                errorMessage: responseBody
            };
        } finally {
            requestBody = rest ? rest.getRequestBody() : "";
        }

        if (this.debug) {
            gs.info("REST Message Response:" +
                "\nMessage: " + restMessage +
                "\nFunction: " + restMethod +
                "\nStatus: " + status +
                "\nRequestBody: " + requestBody +
                "\nResponseBody: " + responseBody);
        }

        return {
            error: (status != "200"),
            errorMessage: (status == "200" ? "" : responseBody),
            status: status,
            result: jObj
        };
    },

 

In the ATF Test, I want to mock out the rest.execute() method so that we are not actually making calls to external services. I have created the following ATF test to try it out:

 

(function(outputs, steps, params, stepResult, assertEqual) {
    describe("WebServiceUtil Script Include Tests", function() {
        it('Should return the ModTrak number if everything goes correctly', function() {

            var restMock = new sn_ws.RESTMessageV2(
                ModTrakAPI.CONSTANTS.API.CREATE.name,
                ModTrakAPI.CONSTANTS.API.CREATE.method
            );
            var mockResponse = {
                getBody: function() {
                    return "12345";
                },
                getStatusCode: function() {
                    return 200;
                }
            };
            spyOn(restMock, "execute").and.returnValue(mockResponse);
			
            var script = new x_###_modtrak.WebServiceUtil(null, true);

            var response = script._rest(
                ModTrakAPI.CONSTANTS.API.CREATE.name,
                ModTrakAPI.CONSTANTS.API.CREATE.method, {}, {},
                restMock
            );

            expect(restMock.execute).toHaveBeenCalled();
            expect(response.modtrak_number).toBe("12345");
        });
    });
})(outputs, steps, params, stepResult, assertEqual);

jasmine.getEnv().execute();

 

According to the SN Docs and Jasmine documentation, this implementation should not call the execute() method on RESTMessageV2, and instead return the mock response object I have configured, but instead when the test runs, when the rest.execute() method is called the following error is thrown:

 

Error: JavaException: java.lang.SecurityException: Method returned an object of type InterpretedFunction which is not allowed in scope x_###_modtrak

 

I have created a HI ticket for this but they are dismissing it as custom code. Has anyone had any luck trying to do the same?

2 REPLIES 2

Any idea what kind of permissions that would be? The code works when tested manually, the call is made and we get a response. It's just when I try to mock out the execute function the error appears.

ab7289
Tera Contributor

I was able to accomplish my goal by using a helper function to wrap the execute method. Updated code and tests as follows:

 

Script Include Code:

    rest: function(
        restMessage,
        restMethod,
        attrs = {},
        attrsNoEscape = {}
    ) {
        if (gs.nil(restMessage) || gs.nil(restMethod)) {
            gs.error("restMessage and restMethod are required");
            return {
                error: true,
                errorMessage: "restMessage and restMethod are required"
            };
        }
        if (this.debug) {
            gs.info("Calling {0} {1}\nattrs: {2}\nattrsNoEscape:{3}",
                restMessage,
                restMethod,
                (!gs.nil(attrs) ? this.json.encode(attrs) : "{}"),
                (!gs.nil(attrsNoEscape) ? this.json.encode(attrsNoEscape) : "{}"));
        }

        if (this.debug) {
            var msg = "Calling :" +
                "\nrestMessage: " + restMessage +
                "\nrestMethod: " + restMethod +
                "\nParams: ";

            Object.keys(attrs).forEach(function(key) {
                msg += "\nkey: " + key + ", value: " + attrs[key];
            });
            msg += "\nParams No Escape: ";
            Object.keys(attrsNoEscape).forEach(function(key) {
                msg += "\nkey: " + key + ", value: " + attrsNoEscape[key];
            });
            gs.info(msg);
        }

        var requestBody,
            responseBody,
            response,
            status,
            jObj;

        try {
            rest = new sn_ws.RESTMessageV2(restMessage, restMethod);

            // set string parameters
            Object.keys(attrs).forEach((k) =>
                !gs.nil(attrs[k]) ?
                rest.setStringParameter(k, attrs[k]) :
                null);
            // set non-escaped string parameters
            Object.keys(attrs).forEach((k) =>
                !gs.nil(attrsNoEscape[k]) ?
                rest
                .setStringParameterNoEscape(k, attrsNoEscape[k]) :
                null);

            // set the host name
            rest.setStringParameter("host",
                gs.getProperty("x_###_modtrak.host"));

            if (!gs.nil(this.mid)) {
                rest.setMIDServer(this.mid);
            }

            response = this._execute(rest);
            responseBody = response.getBody();
            status = response.getStatusCode();
            jObj = this.json.decode(responseBody);
        } catch (ex) {
            var err = "There was an internal error when attempting to contact ";
            err += "The ModTrak REST Service: " + restMessage + ":" + restMethod;
            err += "\nError: " + ex;
            err += "\nError Exception: " + ex;
            if (!gs.nil(response)) {
                err += "\nREST Error Code: " + response.getErrorCode();
                err += "\nREST Error msg: " + response.getErrorMessage();
            }
            gs.error(err);
            responseBody = !gs.nil(ex) ? ex :
                response.getErrorMessage();
            return {
                error: true,
                errorMessage: responseBody
            };
        } finally {
            requestBody = rest ? rest.getRequestBody() : "";
        }

        if (this.debug) {
            gs.info("Workday REST Message Response:" +
                "\nMessage: " + restMessage +
                "\nFunction: " + restMethod +
                "\nStatus: " + status +
                "\nRequestBody: " + requestBody +
                "\nResponseBody: " + responseBody);
        }

        return {
            error: (status != "200"),
            errorMessage: (status == "200" ? "" : responseBody),
            status: status,
            result: jObj
        };
    },

    /**
     * Internal method to enable unit testing
     */
    _execute: function(request) {
        return request.execute();
    },

 

Automated Test Scripts:

(function(outputs, steps, params, stepResult, assertEqual) {
    describe("WebServiceUtil Script Include Tests", function() {
        it('Should return the ModTrak number if everything goes correctly', function() {

            var mockResponse = {
                getBody: function() {
                    return "12345";
                },
                getStatusCode: function() {
                    return 200;
                }
            };

            var script = new x_###_modtrak.WebServiceUtil(null, true);

            var spy = spyOn(script, "_execute");
            spy.and.returnValue(mockResponse);

            var response = script.rest(
                ModTrakAPI.CONSTANTS.API.CREATE.name,
                ModTrakAPI.CONSTANTS.API.CREATE.method, {}, {}
            );

            expect(spy).toHaveBeenCalled();
            expect(response.result).toBe(12345);
            expect(response.error).toBe(false);
            expect(response.errorMessage).toBe("");
        });
 });

})(outputs, steps, params, stepResult, assertEqual);
// uncomment the next line to execute this script as a jasmine test
jasmine.getEnv().execute();