Skip to main content

Using Postman to play with API

ยท 9 min read
Christophe
Markdown, WSL and Docker lover ~ PHP developer ~ Insatiable curious.

Using Postman to play with API

If you are developing your own API (whatever the language) or if you need to consume some, Postman can be really handy.

Calling an API and getting the response is one thing, quite simple in fact, but a nice feature of Postman is the ability to validate the response like making sure the returned type is, f.i. application/json, the HTTP status code is 200, the response body is a JSON object (or an XML string), that the body contains some required information and so on.

You can also validate the response against a given schema to make sure the structure is well the one expected.

In this article, we'll use Postman like a unit test tool i.e. run checks on our own API and make a lot of assertions. This is improving the quality of your code by highlighting potential errors and, for any refactoring you'll do in the future, by running the tests again, you'll make sure you've not broken something; that you don't have any regression. Make sure you've not broken an API when you upgrade some code is gold.

You can download Postman for free here: https://www.postman.com/. You've to create an account before being able to download the program. A documentation is located at https://learning.postman.com/docs/introduction/overview/ so this post won't explain in detail how to use the program but will just give some tips.

Creating an environmentโ€‹

By creating an environment, it's just like creating global variables.

Creating an environment

In the example above, I'm defining my base_url to the URL of my web service.

Make sure to activate the environment (see point 2 on the image).

So, from now, I can create a request and use {{base_url}}

Using the base URL

Creating a collectionโ€‹

If you need to create more than one request, it's best to create a collection (understand a project). You'll be able to store all requests in a collection but the biggest advantage is to be able to set some defaults rules like, f.i. some tests to fire for each request:

Collection with global tests

So, whatever the request I'll start, the four tests below will always be fired:

pm.test("response is ok", function () {
pm.response.to.have.status(200);
});

pm.test("Content-Type header is present", () => {
pm.response.to.have.header("Content-Type");
});

pm.test("Content-Type header is text/xml", () => {
pm.expect(pm.response.headers.get('Content-Type')).to.include('text/xml; charset=utf-8');
});

pm.test("Don't contain any error", function ()
{
pm.expect(pm.response.text()).to.not.include("error");
});

Creating a requestโ€‹

By creating a new request, to pass information in the header, I just need to click on the Headers tab then fill in the key I need to send. In the example of a SOAP request (i.e. called with a XML envelope), I'll need to send a SOAPAction key with the name of the action to start (testFlag here) and I'll specify that the body I'll send is application/xml.

The request headers

Then, since this example is for a SOAP request, I need to send a XML body, as expected by the action:

The request body

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="{{wsdl}}">
<soapenv:Header />
<soapenv:Body>
<ns1:testFlagInput />
</soapenv:Body>
</soapenv:Envelope>

The {{wsdl}} placeholder is a variable defined in the environment.

And, optionally, we can also add a specific test for the request:

The request tests

pm.test("status OK", function () {
var jsonObject = xml2Json(responseBody);
var status = jsonObject["SOAP-ENV:Envelope"]["SOAP-ENV:Body"]["ns1:testFlagOutput"]["status"];
return status == "OK"
});
Replace testFlag by the name of your node

By running the request, in this example, we'll run five tests since we already have defined four tests for the collection.

Result request - Tests

Exporting / importing a collectionโ€‹

By putting all your requests in a collection, you can easily export it (as a .json file)

Exporting a collection

And, of course, import it almost the same way:

Importing a collection

Real-world exampleโ€‹

As seen in the article MS Excel - How to call a SOAP web service, we can call an European web service to check the validity of a VAT number.

Let's try with Postman:

The base url for the service is http://ec.europa.eu/taxation_customs/vies/services/checkVatService, the method has to be POST and we need to set the SOAPAction header to checkVAT.

Check VAT service headers

Then we need to send a specific XML body. For the example, we'll ask for a VAT number in Belgium.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<urn:checkVat xmlns:urn="urn:ec.europa.eu:taxud:vies:services:checkVat:types">
<urn:countryCode>BE</urn:countryCode>
<urn:vatNumber>0403170701</urn:vatNumber>
</urn:checkVat>
</soapenv:Body>
</soapenv:Envelope>

Check VAT service body

By running the request, Postman will return:

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<env:Body>
<ns1:checkVatResponse xmlns:ns1="urn:ec.europa.eu:taxud:vies:services:checkVat:types">
<ns1:countryCode>BE</ns1:countryCode>
<ns1:vatNumber>0403170701</ns1:vatNumber>
<ns1:requestDate>2024-03-05+01:00</ns1:requestDate>
<ns1:valid>true</ns1:valid>
<ns1:name>SA ELECTRABEL</ns1:name>
<ns1:address>Boulevard Simon Bolivar 36 1000 Bruxelles</ns1:address>
</ns1:checkVatResponse>
</env:Body>
</env:Envelope>

Check VAT service response

Some checksโ€‹

I'll use {{wsdl}} as a placeholder

In the examples below, you'll see {{wsdl}} in XML code sample. It's just a placeholder i.e. in a normal situation, you'll retrieve there a URL to a WSDL (or a REST API) service.

Check some metrics like the responseTimeโ€‹

If you wish to validate the response time of a request, f.i. 500 ms:

pm.test('Response time is within an acceptable range', function () {
pm.expect(pm.response.responseTime).to.be.below(500);
})

Controls the HTTP status codeโ€‹

Validate the response status code is 200:

pm.test('Response status code is 200', function () {
pm.expect(pm.response.code).to.equal(200);
})

Ensure the response returns a content-typeโ€‹

pm.test("Content-Type header is present", () => {
pm.response.to.have.header("Content-Type");
});

And if we wish to check the value of the returned content-type:

pm.test("Content-Type header is text/xml", () => {
pm.expect(pm.response.headers.get('Content-Type')).to.include('text/xml; charset=utf-8');
});

Assert the response is a valid XML stringโ€‹

Simple test to ensure we got a valid XML response:

pm.test('Response body is in valid XML format', function () {
const responseData = pm.response.text();
try {
xml2Json(responseData);
pm.expect(true).to.be.true;
} catch (error) {
pm.expect.fail('Response body is not in valid XML format');
}
})

Validate the absence or presence of some wordsโ€‹

Parse the response as a string and make sure the word error isn't present:

pm.test("Don't contain any error", function () 
{
pm.expect(pm.response.text()).to.not.include("error");
});

This example will thus fail as soon as the word error is present in the returned response.

Or, the opposite, make sure some words are present in the response:

pm.test("Assert 'is successful'", function () 
{
pm.expect(pm.response.text()).to.include("is successful");
});

Make assertions on the absence of nodesโ€‹

This assertion is more specific. It will fail as soon as a <error> node is present in the response.

pm.test("The error node shouldn't be part of the response", function () {
const responseData = xml2Json(pm.response.text());
pm.expect(responseData).to.be.an('object');
pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:testOutput']['error']).to.not.exist;
})

If your response is the one below, the assertion will then fail since <ns1:testOutput> contains an <error> node.

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="{{wsdl}}">
<SOAP-ENV:Body>
<ns1:testOutput>
<error>
<!-- ... -->
</error>
</ns1:...>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Make assertions on the presence of nodesโ€‹

As an example, we will ensure the response always have <SOAP-ENV:Envelope> and <SOAP-ENV:Body>:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="{{wsdl}}">
<SOAP-ENV:Body>
<!-- ... -->
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
pm.test('Validate SOAP-ENV:Envelope and SOAP-ENV:Body elements are present', function () {
const responseData = xml2Json(pm.response.text());
pm.expect(responseData).to.be.an('object');
pm.expect(responseData).to.have.property('SOAP-ENV:Envelope');
pm.expect(responseData['SOAP-ENV:Envelope']).to.exist;
pm.expect(responseData['SOAP-ENV:Envelope']).to.have.property('SOAP-ENV:Body');
pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']).to.exist;
})
to.exist is equivalent to to.have.property

pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']).to.exist can also be written like this: pm.expect(responseData['SOAP-ENV:Envelope']]).to.have.property('SOAP-ENV:Body');

For XML, check the value of a given nodeโ€‹

If you wish to ensure a given node has a specific property:

pm.test('Response body has the required fields', function () {
const responseData = xml2Json(pm.response.text());
pm.expect(responseData).to.have.property('SOAP-ENV:Envelope');
pm.expect(responseData['SOAP-ENV:Envelope']).to.have.property('SOAP-ENV:Body');
pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']).to.have.property('ns1:testFlag');
pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:testFlag']).to.have.property('count');
pm.expect(responseData['SOAP-ENV:Body']['ns1:getSessionsLanguagesOutput'].list).to.exist;
})

Assertions for collectionsโ€‹

Let's say your response returns a collection like, below, a list of languages:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="{{wsdl}}">
<SOAP-ENV:Body>
<ns1:getLanguagesOutput>
<list>
<description>English</description>
<iso>en</iso>
<language>E</language>
</list>
<list>
<description>Franรงais</description>
<iso>fr</iso>
<language>F</language>
</list>
<list>
<description>Nederlands</description>
<iso>nl</iso>
<language>N</language>
</list>
</ns1:getLanguagesOutput>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Make sure the list node exists:

pm.test('List array contains at least one element', function () {
const responseData = xml2Json(pm.response.text());
pm.expect(responseData).to.be.an('object');
pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:getLanguagesOutput']).to.have.property('list');
pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:getLanguagesOutput']['list']).to.exist;
})

You wish to make sure there is at least one record:

pm.test('List array contains at least one element', function () {
const responseData = xml2Json(pm.response.text());
pm.expect(responseData).to.be.an('object');
pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:getLanguagesOutput'].list).to.be.an('array').and.to.have.lengthOf.at.least(1);
})

Check every item in the list have expected nodes; for instance:

pm.test('Validate expected structure', function () {
const responseData = xml2Json(pm.response.text());
pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:getLanguagesOutput']).to.have.property('list');
pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:getLanguagesOutput'].list).to.be.an('array').and.to.have.lengthOf.at.least(1);

responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:getLanguagesOutput']['list'].forEach(function (node) {
pm.expect(node.description).to.exist.and.to.not.be.empty;
pm.expect(node.iso).to.exist.and.to.not.be.empty;
pm.expect(node.language).to.exist.and.to.not.be.empty;
});
})

When the <list></list> only contains one element (and thus not an array):

pm.test('Validate expected structure', function () {
const responseData = xml2Json(pm.response.text());
pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:getLanguagesOutput']).to.have.property('list');

const list = responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:getLanguagesOutput']['list']
pm.expect(list).to.have.property('description').and.to.not.be.empty;;
pm.expect(list).to.have.property('iso').and.to.not.be.empty;;
pm.expect(list).to.have.property('language').and.to.not.be.empty;;
})

Assertions on valueโ€‹

Check on the valueโ€‹

Imagine you've something like this and you want to assert that the status is OK:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="{{wsdl}}">
<SOAP-ENV:Body>
<ns1:testFlagOutput>
<status>OK</status>
</ns1:testFlagOutput>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
pm.test("status OK", function () {
var jsonObject = xml2Json(responseBody);
var status = jsonObject["SOAP-ENV:Envelope"]["SOAP-ENV:Body"]["ns1:testFlagOutput"]["status"];
return status == "OK"
});

Same idea but we will make sure the count property is set correctly i.e. to a number.

pm.test("Returned count is a number", function () {
var jsonObject = xml2Json(responseBody);
var count = jsonObject["SOAP-ENV:Envelope"]["SOAP-ENV:Body"]["ns1:testFlagOutput"]["count"];
pm.expect(Number.isInteger(count));
});

If you've an error node in your response, make sure the error code property is not empty:

pm.test('Error code is not empty', function () {
const responseData = xml2Json(pm.response.text());
pm.expect(responseData).to.be.an('object');
pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:testFlag'].error.code).to.exist.and.to.not.be.empty;
})

And if you also have an error message property, make sure it's not empty but contains an error description.

pm.test('Error message is not empty', function () {
const responseData = xml2Json(pm.response.text());
pm.expect(responseData).to.be.an('object');
pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:testFlag']['error']['message']).to.exist.and.to.not.be.empty;
})
Checks on date valueโ€‹

You've a list of employees in a collection and you wish to assert the birthDate:

pm.test('Validate birthDate is null or in a valid date format', function () {
const responseData = xml2Json(pm.response.text());
pm.expect(responseData).to.be.an('object');
pm.expect(responseData['SOAP-ENV:Envelope']['SOAP-ENV:Body']['ns1:getEmployeesOutput']['list']['birthDate']).to.satisfy(function (date) {
return date === null || new Date(date).toString() !== 'Invalid Date';
});
})