Postman: Learn How to Improve Your API Testing Skills

Vector_illustration_for_an_orange_postman

1. Introduction

Postman is a powerful API testing tool that makes it easy for developers to test, develop, and document APIs. This article is designed as a cheat sheet for beginners and experienced developers alike. It will cover variables, assertions, Postman Sandbox, Postman Echo, workflows, commonly used snippets, and mistakes. Let’s dive in!

2. Working with Variables

2.1. Getting Variables in the Request Builder

You can access variables in the Request Builder using double curly braces {{variable_name}}. For example, if you have an environment variable called base_url, you can use it in your request URL like this: {{base_url}}/api/endpoint.

2.2. Global Variables

Global variables are accessible across all environments and collections. To set or get global variables, use the following:

// Set a global variable
pm.globals.set("variable_name", "value");

// Get a global variable
pm.globals.get("variable_name");

2.3. Collection Variables

Collection variables are specific to a collection. To set or get collection variables, use the following:

// Set a collection variable
pm.collectionVariables.set("variable_name", "value");

// Get a collection variable
pm.collectionVariables.get("variable_name");

2.4. Environment Variables

Environment variables are specific to an environment. To set or get environment variables, use the following:

// Set an environment variable
pm.environment.set("variable_name", "value");

// Get an environment variable
pm.environment.get("variable_name");

2.5. Data Variables

Data variables are used when running a collection with a data file (e.g., CSV or JSON). To access data variables, use the pm.iterationData object:

// Get a data variable
pm.iterationData.get("variable_name");

2.6. Local Variables

Local variables are temporary and are only available in the current script. To set or get local variables, use the pm.variables object:

// Set a local variable
pm.variables.set("variable_name", "value");

// Get a local variable
pm.variables.get("variable_name");

2.7. Dynamic Variables

Dynamic variables are randomly generated values. To use dynamic variables, wrap them in double curly braces {{variable_name}} in the Request Builder or use the pm.variables.replaceIn() method in scripts:

// Replace dynamic variables in a string
const url = pm.variables.replaceIn("{{base_url}}/api/{{randomInt}}");

Some common dynamic variables include:

  • {{$guid}}: A random GUID.
  • {{$timestamp}}: The current UNIX timestamp.
  • {{$randomInt}}: A random integer between 0 and 1000.

2.8. Logging and Debugging Variables

To log or debug variables, use the console.log() function:

// Log a variable
console.log(pm.environment.get("variable_name"));

3. Assertions

Assertions are used in Postman to validate API responses. They are written in JavaScript using the Chai Assertion Library.

3.1. Status Code Assertions

To assert the status code of a response, use the pm.response.to.have.status() method:

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

3.2. Response Time Assertions

To assert the response time of a response, use the `pm.expect()` method:

pm.test("Response time is less than 500ms", function () {
  pm.expect(pm.response.responseTime).to.be.below(500);
});

3.3. Header Assertions

To assert the presence of a header or its value, use the pm.response.to.have.header() method:

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

pm.test("Content-Type is application/json", function () {
  pm.response.to.have.header("Content-Type", "application/json");
});

To assert the presence of a cookie or its value, use the pm.cookies.has() and pm.cookies.get() methods:

pm.test("Session cookie is present", function () {
  pm.expect(pm.cookies.has("session")).to.be.true;
});

pm.test("Session cookie has a valid value", function () {
  const cookieValue = pm.cookies.get("session");
  pm.expect(cookieValue).to.match(/^[a-f0-9]{32}$/);
});

3.5. Body Assertions

3.5.1. Any Content Type / HTML Responses

To assert the presence of a text in the response body, use the pm.response.to.have.body() method:

pm.test("Response body contains 'success'", function () {
  pm.response.to.have.body("success");
});

3.5.2. JSON Responses

To assert the value of a JSON property in the response body, use the pm.response.to.have.json() method:

pm.test("User ID is 1", function () {
  const jsonData = pm.response.json();
  pm.expect(jsonData.id).to.equal(1);
});

3.5.3. XML Responses

To assert the value of an XML element in the response body, use the pm.response.to.have.xml() method and the xml2Json() function:

pm.test("Order ID is 1", function () {
  const xmlData = xml2Json(pm.response.text());
  pm.expect(xmlData.order.id._text).to.equal("1");
});

3.6. Skipping Tests

To skip a test, use the pm.test.skip() method:

pm.test.skip("Skipped test", function () {
  // Test code
});

3.7. Failing Tests

To fail a test, use the pm.expect() method with .to.be.false:

pm.test("Failing test", function () {
  pm.expect(true).to.be.false;
});

4. Postman Sandbox

4.1. pm Object

The pm object is a global object in Postman scripts that provides access to request and response data, as well as the ability to manipulate variables and perform tests.

4.2. pm.sendRequest

pm.sendRequest is a method that allows you to send an HTTP request from your scripts. This can be useful for chaining requests, fetching data from external sources, or making API calls within your test scripts.

pm.sendRequest("https://api.example.com/data", function (err, response) {
  if (err) {
    console.error(err);
  } else {
    const data = response.json();
    // Do something with the data
  }
});

5. Postman Echo

Postman Echo is a service that allows you to simulate API requests and responses. It’s useful for testing and learning about different API concepts without having to set up your own API.

5.1. Get Current UTC Time in Pre-request Script

You can use Postman Echo to get the current UTC time in a pre-request script. Here’s an example:

pm.sendRequest("https://postman-echo.com/time/zone?zone=UTC", function (err, response) {
  if (err) {
    console.error(err);
  } else {
    const jsonData = response.json();
    const currentTime = jsonData.formatted;
    pm.environment.set("current_time", currentTime);
  }
});

6. Workflows

Workflows in Postman allow you to control the order of request execution when running a collection.

6.1. Set the Next Request to be Executed

To set the next request to be executed, use the postman.setNextRequest() method:

// In the Tests tab of your request
if (pm.response.to.have.status(200)) {
  postman.setNextRequest("Next Request Name");
} else {
  postman.setNextRequest(null);
}

6.2. Stop Executing Requests / Stop the Collection Run

To stop executing requests or stop the collection run, use the postman.setNextRequest(null) method:

// In the Tests tab of your request
if (pm.response.to.have.status(400)) {
  postman.setNextRequest(null);
}

7. Commonly Used Snippets for API Testing

When testing APIs in Postman, there are some common snippets that can help you quickly validate response data and structure. Let’s take a look at these snippets and their use cases.

7.1. Status Code Check

To check if the response has a specific status code, you can use the following snippet:

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

7.2. Response Time Check

To check if the response time is within an acceptable range, you can use this snippet:

pm.test("Response time is less than 500ms", function () {
    pm.expect(pm.response.responseTime).to.be.below(500);
});

7.3. Content-Type Header Check

To ensure the response has the correct ‘Content-Type’ header, you can use the following snippet:

pm.test("Content-Type is 'application/json'", function () {
    pm.response.to.have.header("Content-Type", "application/json");
});

7.4. JSON Value Check

To check if a specific JSON property has the expected value, you can use this snippet:

pm.test("Response has a 'status' property with the value 'success'", function () {
    const jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property('status');
    pm.expect(jsonData.status).to.equal('success');
});

7.5. Array Length Check

To validate the length of an array in the response, you can use the following snippet:

pm.test("Response has an array of 5 items", function () {
    const jsonData = pm.response.json();
    pm.expect(jsonData).to.be.an('array').that.has.lengthOf(5);
});

7.6. Response Body Contains Text

To check if the response body contains specific text, use the following snippet:

pm.test("Body contains 'example'", function () {
    pm.expect(pm.response.text()).to.include('example');
});

7.7. JSON Object Structure Check

To ensure the JSON response has a specific structure, use this snippet:

pm.test("Response has correct JSON structure", function () {
    const jsonData = pm.response.json();
    pm.expect(jsonData).to.have.all.keys('id', 'name', 'email');
});

7.8. JSON Array Contains Object

To check if a JSON array contains an object with specific properties, use the following snippet:

pm.test("Array contains object with specific properties", function () {
    const jsonData = pm.response.json();
    const targetObject = jsonData.find(item => item.id === 1);
    pm.expect(targetObject).to.include.all.keys('id', 'name', 'email');
});

7.9. Environment Variable Check

To check if an environment variable is set correctly, use this snippet:

pm.test("Environment variable 'token' is set", function () {
    pm.expect(pm.environment.has('token')).to.be.true;
});

7.10. Environment Variable Value Check

To check if an environment variable has the expected value, use the following snippet:

pm.test("Environment variable 'token' has correct value", function () {
    const token = pm.environment.get('token');
    pm.expect(token).to.equal('your_expected_token_value');
});

7.11. Query Parameter Check

To check if the request has a specific query parameter, use this snippet:

pm.test("Request has 'limit' query parameter", function () {
    pm.expect(pm.request.url.query.has('limit')).to.be.true;
});

7.12. Query Parameter Value Check

To check if a query parameter has the expected value, use the following snippet:

pm.test("Query parameter 'limit' has correct value", function () {
    const limit = pm.request.url.query.get('limit');
    pm.expect(limit).to.equal('10');
});

7.13. Response JSON Schema Validation

To validate the response JSON against a schema, use the following snippet:

const Ajv = require('ajv');
const ajv = new Ajv({ allErrors: true });

const schema = {
    "type": "object",
    "properties": {
        "id": { "type": "number" },
        "name": { "type": "string" }
    },
    "required": ["id", "name"]
};

pm.test("Response JSON schema is valid", function () {
    const jsonData

8. Common Mistakes to Avoid in Postman

Here are some common mistakes you should avoid when using Postman:

8.1. Using ‘set’ instead of ‘get’ when defining a variable

Be cautious when using set and get with variables. Using set instead of get when defining a variable can overwrite the value, leading to unexpected results. If you attempt to get the variable afterward, you may receive a null value or another value other than the value intended.

8.2. Not handling asynchronous code correctly

Postman scripts run synchronously by default. If you are using asynchronous code (e.g., setTimeout, fetch, or async/await), you may encounter issues or unexpected results. To handle asynchronous code correctly in Postman, use pm.sendRequest or incorporate the done() callback function in your tests.

In this example will learn how to use asynchronous code correctly in Postman using the pm.sendRequest() function and the done() callback function in your tests:

// Pre-request script
pm.sendRequest("https://api.example.com/data", (error, response) => {
    if (error) {
        console.log(error);
        done(error);
    } else {
        pm.environment.set("data", JSON.stringify(response.json()));
        done();
    }
});

In this pre-request script, we’re making an asynchronous request to https://api.example.com/data using pm.sendRequest(). When the request is complete, the callback function is executed. If there’s an error, we log it and call the done() function with the error. If there’s no error, we set an environment variable with the response data and call done() to indicate the asynchronous operation is complete.

8.3. Using incorrect variable scope

Postman has different variable scopes, such as global, environment, and collection. Make sure to use the appropriate scope for your variables to avoid overwriting values or using the wrong data in your requests and tests.

When variable names conflict in Postman, the application follows a specific order of precedence to determine which value to use. This hierarchy of scopes is as follows:

  1. Local Variables
  2. Data Variables
  3. Environment Variables
  4. Collection Variables
  5. Global Variables

If a variable name is present in multiple scopes, Postman will use the value from the highest priority scope. For example, if you have the same variable name in environment and collection scopes, the environment variable value will be used, as it has a higher priority.

Let’s consider an example:

Global Variable:

pm.globals.set("apiUrl", "https://global.example.com");

Collection Variable:

pm.collectionVariables.set("apiUrl", "https://collection.example.com");

Environment Variable:

pm.environment.set("apiUrl", "https://environment.example.com");

In this case, when you use the {{apiUrl}} variable in your request, Postman will use the value from the environment scope: https://environment.example.com.

To avoid issues related to variable name conflicts, it’s essential to follow best practices when naming variables and use descriptive names that indicate their purpose and scope. Additionally, you can maintain a consistent naming convention across your team to minimize the risk of conflicts.

8.4. Forgetting to update environment variables

If you’re using environment variables in your tests and requests, ensure that you update them regularly. Failing to do so can lead to outdated or incorrect data in your tests, which may cause them to fail.

In this example, we have an API with an access token that expires every 30 days. If you don’t update the access token stored in your environment variable, your API calls will start failing after 30 days.

Let’s consider the following scenario:

You have an environment variable named accessToken:

pm.environment.set("accessToken", "your_access_token");

In your API request, you use the accessToken variable in the Authorization header:

GET /api/v1/resource
Authorization: Bearer {{accessToken}}

You have a test that checks if the response status is 200:

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

If you forget to update the accessToken environment variable when it expires, the API will return an authentication error, and the test checking for a 200 status code will fail.

To avoid this issue, you can either:

  • Set a reminder to update the access token in the environment variable before it expires, or
  • Implement a pre-request script that refreshes the access token automatically if it’s expired.

For example, you can create a pre-request script that checks the token’s expiration date and refreshes it if needed:

const refreshTokenIfNeeded = () => {
    const accessToken = pm.environment.get("accessToken");
    const expirationDate = new Date(pm.environment.get("expirationDate"));

    if (new Date() > expirationDate) {
        // Refresh the access token
        pm.sendRequest({
            url: "https://your_auth_server.com/oauth/token",
            method: "POST",
            body: {
                mode: "urlencoded",
                urlencoded: [
                    { key: "client_id", value: "your_client_id" },
                    { key: "client_secret", value: "your_client_secret" },
                    { key: "grant_type", value: "refresh_token" },
                    { key: "refresh_token", value: pm.environment.get("refreshToken") },
                ],
            },
        }, (err, res) => {
            if (!err) {
                pm.environment.set("accessToken", res.json().access_token);
                const expiresIn = res.json().expires_in;
                const newExpirationDate = new Date();
                newExpirationDate.setSeconds(newExpirationDate.getSeconds() + expiresIn);
                pm.environment.set("expirationDate", newExpirationDate.toISOString());
            } else {
                console.error("Error refreshing access token: ", err);
            }
        });
    }
};

refreshTokenIfNeeded();

8.5. Insufficient error handling

Ensure that you include appropriate error handling in your tests, such as validating the response status code and checking for error messages in the response body. This will help you identify issues quickly and accurately.

In this example, we will create a test suite that checks for various error scenarios in the response, such as invalid status codes, missing fields, or unexpected error messages.

Consider the following API request:

GET /api/v1/resource/{{resourceId}}
Authorization: Bearer {{accessToken}}

Here is a test suite with insufficient error handling:

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

pm.test("Response contains 'data' field", function () {
    const jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property("data");
});

Now, let’s improve the error handling by adding tests for various error scenarios:

// Test for expected status code
pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

// Test for unexpected status codes
const errorStatusCodes = [400, 401, 403, 404, 500];
errorStatusCodes.forEach(function (statusCode) {
    pm.test(`Status code is not ${statusCode}`, function () {
        pm.response.to.not.have.status(statusCode);
    });
});

// Test for the existence of a 'data' field in the response
pm.test("Response contains 'data' field", function () {
    const jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property("data");
});

// Test for common error fields in the response
pm.test("Response does not contain 'error' or 'message' fields", function () {
    const jsonData = pm.response.json();
    pm.expect(jsonData).to.not.have.property("error");
    pm.expect(jsonData).to.not.have.property("message");
});

9. Conclusion

Throughout this article, we’ve covered essential concepts and features in Postman, such as working with variables, writing assertions, utilizing Postman Sandbox, and creating workflows. We’ve also provided examples and code snippets to help you better understand how to use Postman effectively.

To continue learning and improving your skills in API testing and development, consider exploring the following resources and references:

  1. Postman official documentation
  2. Postman community forum
  3. Postman YouTube channel for tutorials
  4. Postman courses on Udemy

Additionally, you can practice by testing and working with public APIs, such as:

  1. Postman Echo
  2. JSONPlaceholder

Finally, don’t forget to keep up-to-date with the latest updates and features in Postman by following their blog and social media accounts:

We hope this guide has been informative and engaging. Keep exploring Postman and practicing your API testing skills to become an expert. Happy testing, and don’t forget to join our mailing list.

https://ahmedradwan.dev

Reach out if you want to join me and write articles with the nerds 🙂


© 2024 · Nerd Level Tech

Categories

Social Media

Stay connected on social media