Postman Test Scripting with XML

Postman Test Scripting with XML

Posted on September 11, 2017 0 Comments

As a MarkLogic developer, I often use Postman to send requests to the MarkLogic server. MarkLogic Server can fit into an architecture in a variety of ways. One way is as a Data Hub Platform. As a data hub, MarkLogic receives data from many data sources, and stores them in a single accessible hub. MarkLogic’s multi-model databases leverage the inherent structure of the data being stored. It provides native storage for JSON, XML, RDF, geospatial, and large binaries so it can easily consume and produce both JSON and XML.

When integrating data from silos, the source data format can be preserved or transformed. Often these services are exposed as REST endpoints for an application tier. In order to test these services, I often use Postman. Previously, I had worked with Postman interactively, testing endpoints with individual requests and getting responses. The new Postman app also supports collections of tests that can be run as a suite.

However, I needed to test the web services of an application with REST endpoints and XML request payloads and responses. The test orchestration needed dynamic payloads, dynamic parameters, and test routing. Here are some of the things I learned on this path which hopefully can save you some time.

Getting Started

To get started, download Postman App. There’s product documentation that’s helpful if you are new to Postman, or for a general introduction. Having used Postman interactively before, it was easy to build a sequence of requests, put them in a collection, and run the collection. Testing a XML REST web services was left as an exercise for the student.

While looking for answers, I found some helpful Postman testing tips. For developing these scripts, I read that the Postman Sandbox is a JavaScript execution environment.

JavaScript code is often used for parsing JSON responses:

var jsonData = JSON.parse(responseBody);
jsonData.payload_root_elem.payload_elem;

For XML responses, I initially headed down a path of using xml2Json. My general online searches led me to think this was the correct way. However, it wasn’t, since it did not solve the problem working with and producing XML. xml2Json did work for getting data from the XML payloads using JSON methods, as shown below:

var xml2JsonData = JSON.xml2Json(responseBody);
xml2JsonData.payload_root_elem.payload_elem;

Or it could be represented as such:

var xml2JsonData = JSON.xml2Json(responseBody);
xml2JsonData['payload_root_elem']['payload_elem'];

So then, I tried to build a new XML payload, and my progress came to a halt– that is, until I found Cheerio.

XML Request and Response Payloads

I wanted and needed to work with XML to get XML values and build new XML payloads. As reported in a GitHub issue, cheerio became the new jQuery support in Postman. Cheerio provides a fast and capable API. It relies on the familiar JQuery API.

This was the magic combination I needed:

var xml = cheerio.load(responseData, {
  ignoreWhitespace: true,
  xmlMode: true
});
console.log(xml.xml()); // Serialize the jQuery as a string
var elementContent = xml("payload_root_elem").find("payload_elem").eq(1).text();

To update the XML:

xml("payload_root_elem").find("payload_elem").eq(1).text("Hello Cheerio");
console.log(xml.xml()); // updated xml as a string

I was off and running to develop my tests. It is certainly possible to create lots of folders with lots of tests and lots of copied and pasted code. I was trying to avoid making tests and making duplicates, was fixing my mistakes, and had to maintain all of the changes I knew were coming as the requirements evolved. Luckily, a combination of Postman features solved these challenges.

Postman Environments

With Postman environment variables, you can create dynamic behaviors. Postman stores the environments in dictionaries for Environment and Global. The keys and values are strings; see below:

postman.setGlobalVariable("key", "value");
postman.getGlobalVariable("key");
postman.setEnvironmentVariable("key", "value");
postman.getEnvironmentVariable("key");

A special syntax {{key}} allows access to these dictionaries values outside of JavaScript tests, such as in the request URL. The variable key is evaluated first in the Environment context and then in the Global context so a key in both will result in the value from the Environment.

Dynamic Requests

Postman supports dynamic values in the request with access to variables in the environment. This special syntax {{key}}, mentioned previously, allows you to use dynamically evaluated URLs, authentication usernames and passwords, and payloads. All of these can be configured to make accessing local, developer, and QA servers easier.

Here’s what you need to do to set up a “Pre-request Script”:

if (!("ml_url" in environment)) {
  postman.setEnvironmentVariable("ml_url", "http://localhost:8080");
}
postman.setEnvironmentVariable("ws_endpoint", "recipie");

And then you can configure your test URL with something like:

{ml_url}}/v1/resources/{{ws_endpoint}}?rs:myParam=test

Note: I had trouble separating the scheme, host, and port number into individual variables, but found that when combined, they worked.

By testing the environment with:

 if (!("ml_url" in environment)) {...}

you can override these values by creating and using named Postman environments for local, dev, and qa.

Storing and Evaluating Functions with Environment Variables

Another powerful capability is storing JavaScripts or functions in environment variables to promote reuse. Place some code in a string and store it in an environment and use it with eval.

postman.setGlobalVariable("routingTable", (current_test) => {
  var next_test;
  if (current_test === "foo") {
    next_test = "bar";
  }
  return next_test;
});
eval(globals.routingTable(current_test);

Creating a script to clean your variables at the start of your tests can help avoid unintended consequences of defined environment values in subsequent tests.

Credit goes to the postman-app GitHub page for the following:

postman.setGlobalVariable("module:prefixedScopeVars", "clearPrefixFromScope=function(a,b,c){_.each(_.filter(_.keys(a),function(a){return a.startsWith(b)}),function(a){c(a)})},clearPrefixFromEnv=function(a){clearPrefixFromScope(environment,a,postman.clearEnvironmentVariable.bind(postman))},clearPrefixFromGlobal=function(a){clearPrefixFromScope(globals,a,postman.clearGlobalVariable.bind(postman))},anyPrefixedInScope=function(a,b){return _.some(_.keys(a),function(a){return a.startsWith(b)})},anyPrefixedInEnv=function(a){return anyPrefixedInScope(environment,a)},anyPrefixedInGlobal=function(a){return anyPrefixedInScope(globals,a)},function coalesce(){var len = arguments.length; for (var i=0; i<len; i++) {if (arguments[i] !== null && arguments[i] !== undefined) {return arguments[i];}}return null;}");
eval(postman.getGlobalVariable("module:prefixedScopeVars"));

// Clear variable to start over
clearPrefixFromGlobal("gws_"); //clean the Global workspace
clearPrefixFromEnv("ws_"); //clean the Environment workspace

If you like this sort of approach, consider reading Writing a Behavior-Driven API testing Environment within Postman to get a better understanding of what we just did with some examples.

Run a Script

The Postman echo server echoes the HTTP headers, request parameters, payload, and the complete URI requested. I find it useful to use the echo server to test and run scripts for routing and updating environment variables without calling the end-user application.

When I just want to execute a script without calling an endpoint, I create a test with a script and call https://postman-echo.com/get

Alternatively, if you don’t want to talk to an outside server, you can use an idempotent endpoint of the MarkLogic server, such as a HEAD request to: http://localhost:8001

Or, you can send a GET request to your application port on NNNN: http://localhost:NNNN/v1/config/resources

Routing Tables

Under the usual conditions, Postman runs the test requests in the order they exist in the folder. You can change this behavior by calling postman.setNextRequest("testName"). Postman has setNextRequest to route to a specific named next request. It is honored at the end of the current request execution.

postman.setNextRequest("Suite1");

Using an empty name halts execution.

postman.setNextRequest("");

You can mix the sequential execution of tests without routing directives with tests that include routing to create sophisticated workflows.

Note: Watch out for duplicate named tests. Postman picks the last one in the collection. Duplicate names can waste your time debugging and looking for the problem.

The benefit of setNextRequest is that it provides the opportunity for looping and flow of control in request executions.

postman.setGlobalVariable("suiteRoutingTable", (test_endpoint, current_test) => {
    var next_suite;

    if (typeof environment.ws_test_endpoint === 'undefined') {
      console.log("ws_test_endpoint began as:"+"undefined");
      postman.setEnvironmentVariable("ws_test_endpoint", "start");
    }
    if ( environment.ws_test_endpoint === "") {
      postman.setEnvironmentVariable("ws_test_endpoint", "start");
    }
    console.log("ws_test_endpoint is now:"+environment.ws_test_endpoint);

    switch(environment.ws_test_endpoint) {
    /* Single endpoint routing
      case "start":
        next_suite = "Suite1";
        break;
      case "Suite1":
        next_suite = ""; // Used to halt execution here
        break;
    */
      case "start":
        next_suite = "Suite1";
        break;
      case "Suite1":
        next_suite = "Suite2";
        break;
      case "Suite2":
        next_suite = "";
        break;
      default:
        next_suite = "";
    }

    postman.setEnvironmentVariable("ws_test_endpoint", next_suite);
    console.log("new ws_test_endpoint is:"+environment.ws_test_endpoint);

Combining Approaches

With the following, look up the current routing step to find the next step, set up that step, and proceed to the next request:

var next_test = eval(globals.routingTable)(environment.ws_test_endpoint, environment.ws_current_test);
postman.setEnvironmentVariable("ws_current_test", next_test);
postman.setNextRequest(next_test);

I created individual tests for POST "create", GET "retrieve", and PUT "update". I read and updated XML payloads, made the query parameters dynamic using {{key}}, and lastly used routing to apply them to different endpoints.

As part of a team using GitHub, I found the information in Postman: Effectively Storing and Using Tests in a Git Repository helpful to know.

Note Bene

For some Postman versions, either F11 or the View tab’s “Toggle Full Screen Mode” will cause app to enter the full screen and not be able to exit. A reinstall will correct this problem, but you will lose your collections and history. Editing the configuration can restore the view.

Further Reading

  • MLU On Demand Video Tutorial: Creating a REST Extension — This tutorial covers the basics of REST, and how to code and deploy an extension. The tutorial also demonstrates utilizing the extension with MarkLogic’s Node.js Client api.
  • Written Tutorial: Learning the MarkLogic REST API— This tutorial will walk you through a series of “How-Tos” for working with MarkLogic exclusively through its REST API.

Bob Starbird

View all posts from Bob Starbird on the Progress blog. Connect with us about all things application development and deployment, data integration and digital business.

Comments

Comments are disabled in preview mode.
Topics

Sitefinity Training and Certification Now Available.

Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.

Learn More
Latest Stories
in Your Inbox

Subscribe to get all the news, info and tutorials you need to build better business apps and sites

Loading animation