Basics of Post-Response Scripting in Postman
Published on January 7, 2025, by Roman Ďurek
Why Use Post-Response Scripting?
Post-response scripting in Postman is a game-changer for API testing and quality assurance. By embedding scripts in the Tests tab of a request, you can:
- Validate API Responses Automatically: Ensure that data types, values, and status codes are correct without manual checks.
- Chain Requests Dynamically: Extract data from responses and use it in subsequent requests, enabling complex workflows.
- Improve Test Consistency and Reliability: Automate repetitive tasks, reducing human error and ensuring uniformity in tests.
- Enhance Error Detection: Detect anomalies early by defining detailed assertions and schemas for response structures.
These capabilities make post-response scripting an indispensable part of effective API testing, enabling comprehensive validation and automation to ensure robust APIs.
Prerequisites
Before diving into this tutorial, you should have a basic understanding of both JavaScript and Postman. Specifically, you should be familiar with:
- Basic JavaScript concepts such as variables, functions, and promises.
- Sending requests, checking responses, understanding basic Postman functionality like using console, setting headers, query parameters, and body data.
If you’re new to Postman or JavaScript, we recommend going through introductory resources first. Once you’re comfortable with the basics, you’ll be ready to explore more advanced Postman scripting techniques like the ones covered here.
Getting Started with Post-res Scripts
Postman provides a built-in JavaScript environment for writing test scripts. You can access this feature by navigating to the Scripts tab in any request and select Post-res.
Basic Scripting Structure
A simple test script example:
pm.test("Status code is 200", () => {
pm.response.to.have.status(200);
});
This script checks if the response status code is 200.
Explanation of pm.test() function:
The pm.test() function is used to define a test in Postman. It takes two arguments:
- A string describing the test.
- A callback function that contains the test's logic.
Key Post-Response Scripting Techniques
1. Status Code Checks
Validating the status code ensures that the API behaves as expected.
pm.test("Response status is 200", () => {
pm.response.to.have.status(200);
});
For a range of status codes:
pm.test("Status is client error (400-499)", () => {
pm.expect(pm.response.code).to.be.within(400, 499);
});
To check if the status code is one of several values:
pm.test("Status is one of expected values", () => {
pm.expect(pm.response.code).to.be.oneOf([200, 201, 202, 204]);
});
2. Validating Response Data with pm.expect()
Here’s a fake response example to show what data we are validating:
To work with the response data, declare it once at the top of this section:
const response = pm.response.json();
Checking Data Types
pm.test("Age a number", () => {
pm.expect(response.age).to.be.a("number");
});
pm.test("Name is string and is not empty", () => {
pm.expect(response.name).to.be.a("string").and.not.be.empty;
});
Checking Multiple Data Types
// using typeof
pm.test("Response body contains a number", () => {
pm.expect(typeof response.age).to.be.oneOf(["number", "string", "undefined"]);
});
Matching String Values
pm.test("User name is Alice", () => {
pm.expect(response.name).to.eql("Alice");
});
Array Length Validation
pm.test("User has 3 relatives", () => {
pm.expect(response.relatives).to.be.an("array").with.lengthOf(3);
});
Checking for Undefined Values
pm.test("Email is not undefined", () => {
pm.expect(response.email).to.not.be.undefined;
});
Validating Boolean Values
pm.test("User is active", () => {
pm.expect(response.isActive).to.be.true;
});
Partial Text Matching
pm.test("Response status contains 'success'", () => {
pm.expect(response.status).to.include("success");
});
Object Property Existence
// This test checks whether the object response directly contains the "host" property
// The property must be an own property of the response object, meaning it is not inherited from the prototype chain.
pm.test("Response has a 'token' property", () => {
pm.expect(response).to.haveOwnProperty("token");
});
//This test checks whether the response object has a property named "host", either as an own property or inherited from its prototype.
// It doesn't matter if the property is inherited from the prototype chain. If the property exists on response (either directly or through inheritance), this test will pass.
pm.test("Response has a 'token' property", () => {
pm.expect(response).to.have.property("token");
});
Validating String in Entire Response
pm.test("Response body contains expected text", () => {
const responseBody = pm.response.text();
pm.expect(responseBody).to.include("success");
});
Combining Multiple Assertions
pm.test("Check user details", () => {
pm.expect(response.name).to.be.a("string").and.to.include("Alice");
pm.expect(response.age).to.be.within(18, 65);
pm.expect(response.email).to.match(/@example\.com$/);
});
Testing headers
pm.test("Content-Type is application/json", () => {
pm.expect(pm.response.headers.get("Content-Type")).to.include(
"application/json",
);
});
pm.test("Server header is present", () => {
pm.expect(pm.response.headers.has("Server")).to.be.true;
});
pm.test("X-Powered-By does not reveal technology", () => {
const poweredBy = pm.response.headers.get("X-Powered-By");
pm.expect(poweredBy).to.not.match(/(php|asp|node)/i);
});
3. JSON Schema Validation
JSON Schema is a standard for describing the structure and validating the content of JSON data. It defines the expected data types, required fields, and nested object relationships within a response. In Postman post-response scripting, JSON Schema validation plays a crucial role in ensuring that your API responses conform to expected formats, which enhances reliability and consistency.
Key Components of a JSON Schema
- Type Validation: Defines the data types (e.g., string, number, object, array, boolean, etc.).
- Required Fields: Specifies mandatory properties.
- Property Constraints: Imposes conditions on property values, such as string length, minimum and maximum values for numbers, or pattern matching.
- Nested Objects and Arrays: Supports recursive validation for complex data structures.
Example Schema
const schema = {
type: "object",
additionalProperties: false,
properties: {
id: { type: "integer" },
name: { type: "string" },
isActive: { type: "boolean" },
tags: {
type: "array",
items: { type: "string" },
},
profile: {
type: "object",
additionalProperties: false,
properties: {
email: { type: "string", format: "email" },
age: { type: "number" },
addresses: {
type: "array",
items: {
type: "object",
additionalProperties: false,
properties: {
type: { type: "string" },
city: { type: "string" },
zip: { type: "string" },
},
required: ["type", "city", "zip"],
},
},
},
required: ["email", "age", "addresses"],
},
scores: {
type: "array",
items: { type: "integer" },
},
preferences: {
type: "object",
additionalProperties: false,
properties: {
notifications: {
type: "object",
additionalProperties: false,
properties: {
email: { type: "boolean" },
sms: { type: "boolean" },
},
required: ["email", "sms"],
},
},
required: ["notifications"],
},
skills: {
type: "array",
items: {
type: "object",
additionalProperties: false,
properties: {
name: { type: "string" },
level: { type: "string" },
},
required: ["name", "level"],
},
},
},
required: ["id", "name", "isActive", "tags", "profile", "scores", "preferences", "skills"],
};
pm.test("Response matches the expected JSON schema", () => {
pm.response.to.have.jsonSchema(schema);
});
- additionalProperties: false - This keyword is critical when you want to control whether an object can contain properties that are not specified in the schema. From my experience i can tell this is essential when testing new API's, there might be typo in some props and this way you can easily catch it.
- required: [] - This helps define which properties are mandatory for an object to be considered valid. By listing property names under the "required" keyword, you ensure that those properties must appear in the object.
Important Note: In API documentation you'll always find which props are required in response.
Incorporating JSON Schema validation into your post-response scripts builds a solid foundation for reliable, scalable, and maintainable API testing.
For more information check JSON Schema Reference
Advanced Tips
In this section, we delve into more intricate test cases, demonstrating how to work with a variety of data types and ensuring robust validation through a comprehensive, simulated API response.
Iterating Over Arrays with for Loop
When working with arrays in JSON responses, it's often useful to iterate through the elements and validate each item individually. Below are different techniques for testing array elements.
Using a for loop to include indexes
If you need to reference the index of each item in the array (for instance, to create dynamic test cases for each item), you can use the entries() method. The following example demonstrates how to generate a separate test for each tag in the tags array:
// This loop checks if each tag is a string, using the index to label each test
for (const [index, tag] of response.tags.entries()) {
pm.test(`Tag ${index} is a string`, () => {
pm.expect(tag).to.be.a("string");
});
}
Validating array elements without the index
If the index is not necessary for your test logic, you can iterate through the array without it. For example, to check that all scores are valid numbers, you can simplify your loop like so:
pm.test("Scores are valid numbers", () => {
for (const score of response.scores) {
pm.expect(score).to.be.a("number");
}
});
Using the index as a condition for specific array elements
In some cases, you might want to test only specific elements based on their position in the array. For example, you could test only the second address in the addresses array to ensure it contains certain properties:
pm.test("Address has type and city", () => {
for (const [index, address] of response.profile.addresses.entries()) {
if (index > 0) {
// Only validate the second address
pm.expect(address).to.have.property("type");
pm.expect(address.city).to.be.a("string");
}
}
});
There are numerous ways to test arrays in API responses, each serving a distinct purpose depending on the structure and requirements of your data. The examples provided above are just a few approaches to iterate over arrays, but they highlight the flexibility and depth that can be achieved in API testing. Feel free to experiment with these techniques to suit your specific use cases.
Use Array Methods
Additionally, you may want to explore other array methods to further streamline your validation and testing processes. These functions can be particularly useful for more complex scenarios, allowing you to transform, filter, and aggregate data efficiently before applying tests. For instance:
- .find(): This function is invaluable for situations where you want to search for a specific item within an array that meets a certain condition. It returns the first element that satisfies the provided testing function, making it perfect for quick lookups and validations.
- .map(): Useful for transforming each element in an array, allowing for more customized validation or transformation of data.
- .filter(): Ideal for extracting specific subsets of data based on conditions, enabling focused validation of particular items.
- .reduce(): Excellent for accumulating values, which can be useful for cases where you need to perform validations on aggregated data (e.g., checking sums or averages).
- .forEach(): A simpler alternative to the for loop, it executes a provided function once for each array element.
- .some(): Use .some() when you need to check whether at least one element in the array satisfies a condition. This can be useful for validating that some aspect of the data is correct, such as ensuring that at least one tag matches a certain value or that one address has the correct city.
- .every(): Similar to .some(), but .every() checks whether all elements in the array meet the condition. This is great for validating that every element in an array, such as scores or tags, meets the expected criteria.
Experimenting with these array functions can help you write more concise, efficient, and readable tests while improving the flexibility of your API testing strategies.
Check all methods on Array - JavaScript - MDN Web Docs
Create custom functions
Using custom functions in Postman scripts simplifies your tests and improves efficiency. They reduce repetitive code by encapsulating common logic into reusable blocks, making scripts easier to read and maintain. Custom functions also help isolate and debug errors by centralizing error handling, providing clearer logs. With parameterization, they allow dynamic, flexible tests that adapt to various data. Overall, functions enhance test reusability, readability, and scalability, leading to more robust API validations.
Example: Check Unique Property in an Array of Objects
const response = pm.response.json();
const skills = response.skills;
const addresses = response.profile.addresses;
const hasArrayDuplicates = (array, property) => {
const unique = new Set();
let hasDuplicates = false;
array.forEach((item) => {
const value = item[property];
if (unique.has(value)) {
console.error(`Duplicate object found: ${item}`);
hasDuplicates = true;
} else unique.add(value);
});
return hasDuplicates;
};
pm.test("All skills are unique", () => {
pm.expect(hasArrayDuplicates(skills, "name")).to.be.false;
});
pm.test("All addresses are unique", () => {
pm.expect(hasArrayDuplicates(addresses, "type").to.be.false);
});
Postman Packages
Starting from Postman v11.23.3+, the platform introduces the ability to utilize packages for enhanced script management and reusability. This feature allows users to store commonly used scripts and tests in a centralized Package Library, enabling seamless reuse across personal, private, and team workspaces. Packages can be easily imported into both Pre-request and Post-response tabs, streamlining workflow efficiency and promoting consistency in API testing practices.
Additionally, packages offer the flexibility to store virtually any custom function or reusable logic, making it possible to encapsulate complex operations, utility functions, or shared test scripts. Once defined, these elements can be seamlessly integrated into any collection or individual request, eliminating redundancy and enhancing maintainability. This approach simplifies test management by centralizing logic, ensuring consistency, and promoting best practices across multiple projects and workspaces.
To add new Package please follow official docs Reuse scripts in Postman or check my previous blog where i covered creating of packages Basics of Pre-request scripting in Postman
Important note: The number of packages you can store in the Package Library depends on your Postman plan. With free plan you are allowed to make 3 packages.
Once you have created and stored your custom functions in a package, you can use them within Post-response scripts by importing individual functions or the entire package. This flexibility enhances code reuse and consistency across multiple requests or collections.
Example 1: Importing a Specific Function
// Importing a specific function from a package
const { validateEmailFormat } = pm.require("my-team-package-library");
// Using the imported function
pm.test("Email format is valid", () => {
const email = pm.response.json().profile.email;
pm.expect(validateEmailFormat(email)).to.be.true;
});
Example 2: Importing the Whole Package
// Importing the entire package
const myPackage = pm.require("my-team-package-library");
// Using a function from the imported package
pm.test("Email format is valid", () => {
const email = pm.response.json().profile.email;
pm.expect(myPackage.validateEmailFormat(email)).to.be.true;
});
By choosing between these two approaches, you can tailor your imports to your use case, keeping your scripts clean and efficient while maximizing maintainability and readability.
Try it by yourself
If you wanna prcatice by yourself I've created an endpoint for you. This endpoint demonstrates a comprehensive example of a JSON response containing various data types, nested objects, and arrays. It is intended for educational or reference purposes, highlighting the structure and versatility of API responses.
[GET] https://api.bwrd.eu/data-types
Here are some test you can try to write for this response:
- Write a test to check if username is "GamerMaster42"
- Write a test to check if there are at least 10 friends in the friends array
- Check if game "CyberQuest: Awakening" is installed
- Calculate the total playtime across all recent games and console.log the result
- Write a test to check if an achievement with the name "Speedrunner" is present in the achievements array
- Check metadata in one test - the requestId and timestamp are not empty strings + timestamp is a valid date
- Console.log the usernames of all friends with the status "online"
- Write a test to check that each game in recentGames has a platform array with at least one entry.
- Test if the rayTracing feature is disabled in graphics settings
- Verify that user is online by checking the isOnline property. 11.Check If a Specific Action Has a Keybinding. Verify if the "reload" action has a key assigned.
- Verify Unique Keybindings. Ensure that no two actions are assigned the same key.
Best Practices for Postman Scripts
- Use Meaningful Test Descriptions: Descriptions should clearly state what each test validates.
- Modularize JSON Schemas: Store complex schemas in external files or environment variables for reuse.
- Check for Optional Properties Safely: Use conditions to validate optional fields without causing errors.
- Group Related Tests Organize related
pm.test()blocks together for readability. - Minimize Duplication: Extract common variables or logic into reusable snippets.
- Validate Response Time: Include performance checks to ensure API speed meets requirements.
pm.test("Response time is less than 200ms", () => {
pm.expect(pm.response.responseTime).to.be.below(200);
});
Conclusion
Postman’s post-response scripting capabilities elevate API testing by adding powerful automation, flexible data validations, and robust error handling. Implementing best practices and advanced examples from this guide will help ensure comprehensive test coverage. Mastering these skills also enhances API reliability and maintainability, making your testing strategy more effective and professional.
Ready to enhance your API testing skills? Start applying these post-response scripting techniques in your projects today! Explore Postman's sample collections or create your own API tests using real-world endpoints. Integrate validations, automate test flows, and ensure your APIs meet performance and reliability standards. Build comprehensive test suites and maintain confidence in the quality of your APIs. Join the active Postman community to share insights, learn best practices, and stay updated on the latest tools and features.
By following these techniques, you’ll master post-response scripting in Postman, enhance your API testing workflow, and develop more maintainable, powerful tests for robust API solutions.