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.
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.
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.
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.
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.
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.
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
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.
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);
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.
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.
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.
Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.
Learn MoreSubscribe to get all the news, info and tutorials you need to build better business apps and sites