API testing with Postman

For a project I’m currently working on we are using the well-known tool Postman to test the http API of a service that we are developing and maintaining. I have been using it to test single requests in the past, but as it comes with a lot of features and can feel a bit bloated I have resorted to other tools for simple use cases.

The requirement for my project was to provide an easy way for management and technical staff to send typical requests to our service in different environments. Our service requires authentication through JSON Web Tokens and the solution should handle this as conveniently as possible.

Results from requests should automatically be validated, so that normally no detailed look into the response is necessary.

Example request

For demo purposes we will create one request only. Postman organizes requests into Collections, we will name it after the service under test. The service exposes an endpoint /cars and requires some HTTP headers to be sent. It looks something like this.

Screenshot of the Postman UI showing the Collections view.

If you take a closer look, you’ll notice a variable {{host}} in the URL on the right hand side of the image. What’s that about? There’s different scopes for variables in Postman and you can learn all about it in the Postman docs. For our current use case, let’s first talk about environments.

Environments

I’ve mentioned before that we want to send our requests to multiple environments. As a developer I have my local environment, for example. But we also have an integrated development environment and multiple staging environments for our customers. Postman enables us to mange these environments, in fact you can see a navigation item labled “Environments” on the left side of the image. Let’s go there.

Screenshot of the Postman UI showing the Environments view.

Here, the Local environment is selected and you can see three variables defined. The first one is named host. Its the one used in our request above. Other environments define the same variables but assign different values. The other variables are used for authenticating requests. We’ll see them used later.

You can choose the active environment easily in the top right corner of the Postman UI.

Screenshot of the Postman UI with the expanded "Environment" switcher visible.

Authentication of requests

Our service looks for a JSON Web Token in the request, it is sent using the Authorization HTTP header. You can do this manually by creating that header, or you can use the Authorization tab. As we need the authorization working for all our requests, we will not define it on our demo request, but on the collection itself. Postman will then apply it to all requests in that collection.

Screenshot of the Postman UI showing the Authorization view of a Collection.

We select Bearer Token as the authorization type and as value we reference the variable {{access_token}}. This variable is set through a pre-request script written in Javascript.

let url_auth = pm.variables.get("keycloak_url")+"/auth/realms/" + pm.variables.get("keycloak_realm") + "/protocol/openid-connect/token";
let params = "client_id=" + pm.variables.get("keycloak_client_id")
+ "&client_secret=" + pm.variables.get("keycloak_client_secret")
+ "&grant_type=client_credentials";
pm.sendRequest(
{
url: url_auth,
method: 'POST',
header: 'Content-Type: application/x-www-form-urlencoded',
body: {
mode: 'raw',
raw: params
}
}, (_, response) => {
let access_token = response.json()['access_token']
pm.environment.set("access_token", access_token)
});
view raw pre-request.js hosted with ❤ by GitHub

This script uses more variable defined in the environments (remember the keycloak variables above) to setup the url and required parameters for authentication. It then sends a request to that url, extracts the resulting access token and stores it in the current environment’s variables.

With that in place, any request that we add to our collection will automatically and transparently first send a request to obtain an access token and then use that token to send the actual request. Pretty neat.

Testing request responses

Postman provides an API to test our requests, there’s extensive documentation available, as well. You can write tests in the Test tab and the API is accessible throught the Postman API object pm. Have a look at the ChaiJS BDD syntax, if you don’t know it, yet, as it is being used by Postman. Here’s an example for our demo request.

let jsonData = JSON.parse(responseBody)
pm.test("Cars have been received", () => {
pm.response.to.have.status(200)
pm.expect(jsonData).to.not.be.undefined
pm.expect(jsonData).to.be.an('array')
pm.expect(jsonData).to.not.be.empty
});

You can see that we first extract the JSON data from the HTTP response, conveniently provided to us as an implicit responseBody object. Afterwards we define our tests, in this case we check for a 200 HTTP status and add various tests to make sure that we received reasonable data.

Summary

Postman is a really neat tool to implement these kind of tests and you can do even more like defining mock servers, monitors and flows. I have not specifically mentioned it above, but as Postman comes with an extensive user management, you can share all your Postman assets with your coworkers. For up to three team members you don’t even have to pay. We currently have no need for these features, but I’ll probably check them out soon, anyway. Postman still feels a bit bloated for manually testing a few requests, but for more complex use cases it can bring benefit to a project.