Testing APIs with Postman is a crucial part of ensuring the reliability and correctness of your endpoints. However, manually writing tests for every property in your API responses can be tedious and error-prone. Moreover, when tests fail, it can be challenging to identify the exact reason for the failure. This article will walk you through creating a generic validation function in Postman to streamline your testing process and make debugging easier.
When writing tests for an API response in Postman, you often need to validate various properties and their types. This involves writing multiple pm.test
statements, which can become repetitive and cumbersome. For example:
pm.test("Response status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Content-Type is application/json", function () {
pm.expect(pm.response.headers.get("Content-Type")).to.include("application/json");
});
pm.test("Product ID is a string", function () {
const responseData = pm.response.json();
pm.expect(responseData.product.id).to.be.a('string');
});
// And many more similar tests...
This approach not only clutters your test scripts but also makes it difficult to maintain them. Additionally, when a test fails, identifying the root cause can be challenging, especially when dealing with nested structures.
To address these issues, you can create a generic validation function that checks the structure and types of your API response properties. This function will simplify your test scripts and provide clearer error messages when tests fail.
The validation function will accept an object, a list of properties to validate, and an optional parent key for nested structures. It will iterate over the properties and validate their types and existence.
const validateSchema = (object, properties, parentKey = "") => {
pm.expect(object).to.be.an('object', `"${parentKey}" object is missing or not an object`);
properties.forEach(prop => {
const currentKey = parentKey ? `${parentKey}.${prop.key}` : prop.key;
if (prop.subproperties && Array.isArray(object[prop.key])) {
object[prop.key].forEach((subItem, index) => {
prop.subproperties.forEach(subprop => {
pm.test(`Property "${currentKey}[${index}]" => "${subprop.key}" is of type ${subprop.type}`, function () {
pm.expect(subItem[subprop.key]).to.exist.and.to.be.a(subprop.type, `Property "${currentKey}[${index}]" => "${subprop.key}" is missing or not a ${subprop.type}`);
});
});
});
} else {
pm.test(`Property "${currentKey}" => "${prop.key}" is of type ${prop.type}`, function () {
if (prop.type === 'null') {
pm.expect(object[prop.key]).to.equal(null, `Property "${currentKey}" => "${prop.key}" should be null but is not`);
} else {
pm.expect(object[prop.key]).to.exist.and.to.be.a(prop.type, `Property "${currentKey}" => "${prop.key}" is missing or not a ${prop.type}`);
}
});
}
});
};
Next, define the schema you want to validate. This schema will be an array of objects, each representing a property to validate. For nested properties, you can include subproperties.
const schema = [
{
key: 'product',
properties: [
{ key: 'id', type: 'string' },
{ key: 'category', type: 'string' },
{ key: 'source', type: 'string' },
{ key: 'domain', type: 'string' },
{ key: 'title', type: 'string' },
{ key: 'conclusion', type: 'string' },
{ key: 'images', type: 'array' },
{ key: 'locale', type: 'string' },
{ key: 'product', type: 'object' },
{ key: 'description', type: 'string' },
{ key: 'rules', type: 'array' },
{ key: 'ver', type: 'string' }
]
},
{
key: 'reviews',
properties: [
{ key: 'reviewsSummary', type: 'array' },
{ key: 'error', type: 'null' },
{ key: 'reviewsImages', type: 'array' },
{ key: 'ver', type: 'string' },
{ key: 'totalReviews', type: 'string' },
{ key: 'rating', type: 'string' }
]
},
{
key: 'product.product.price',
properties: [
{ key: 'currency', type: 'string' },
{ key: 'maxPrice', type: 'number' },
{ key: 'minPrice', type: 'number' },
{ key: 'price', type: 'array' },
{
subproperties: [
{ key: 'date', type: 'string' },
{ key: 'price', type: 'number' },
{ key: 'minPrice', type: 'number' },
{ key: 'maxPrice', type: 'number' }
]
}
]
}
];
Now, you can use the validation function in your Postman tests. This will simplify your test scripts and provide clear error messages when tests fail.
pm.test("Response status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Content-Type is application/json", function () {
pm.expect(pm.response.headers.get("Content-Type")).to.include("application/json");
});
pm.test("Images array should not be empty", function () {
const responseData = pm.response.json();
pm.expect(responseData.product.images).to.be.an('array').and.to.have.lengthOf.at.least(1, "Images array should not be empty");
});
pm.test("Response time is within an acceptable range", function () {
pm.expect(pm.response.responseTime).to.be.below(40000);
});
pm.test("Validate the response schema", function () {
const responseData = pm.response.json();
schema.forEach(test => {
const object = test.key.split('.').reduce((o, i) => o ? o[i] : undefined, responseData);
validateSchema(object, test.properties, test.key);
});
pm.test("Property wasCashed is a boolean value", function () {
pm.expect(responseData.wasCashed).to.be.a('boolean', 'Property "wasCashed" is not a boolean or missing');
});
pm.test("Validate the error schema", function () {
pm.expect(responseData.error).to.equal(null, 'Property "error" should be null but is not');
});
});
pm.test("Validate the schema", function () {
const responseData = pm.response.json();
const tests = [
{
key: 'product',
properties: [
{ key: 'id', type: 'string' },
{ key: 'category', type: 'string' },
{ key: 'source', type: 'string' },
{ key: 'domain', type: 'string' },
{ key: 'title', type: 'string' },
{ key: 'conclusion', type: 'string' },
{ key: 'images', type: 'array' },
{ key: 'locale', type: 'string', value:'en' },
{ key: 'product', type: 'object' },
{ key: 'description', type: 'string' },
{ key: 'rules', type: 'array' },
{ key: 'ver', type: 'string' }
]
},
{
key: 'reviews',
properties: [
{ key: 'reviewsSummary', type: 'array' },
{ key: 'error', type: 'null' },
{ key: 'reviewsImages', type: 'array' },
{ key: 'ver', type: 'string' },
{ key: 'totalReviews', type: 'string' },
{ key: 'rating', type: 'string' }
]
},
{
key: 'product.product.price',
properties: [
{ key: 'currency', type: 'string' },
{ key: 'maxPrice', type: 'number' },
{ key: 'minPrice', type: 'number' },
{ key: 'price', type: 'array' }
],
subproperties: [
{ key: 'date', type: 'string' },
{ key: 'price', type: 'number' },
{ key: 'minPrice', type: 'number' },
{ key: 'maxPrice', type: 'number' }
]
}
];
tests.forEach(test => {
const object = test.key.split('.').reduce((o, i) => o ? o[i] : undefined, responseData);
pm.test(`Validate the "${test.key}" schema`, function () {
pm.expect(object).to.be.an('object', `"${test.key}" object is missing or not an object`);
test.properties.forEach(prop => {
if (prop.key === 'price' && Array.isArray(object[prop.key])) {
object[prop.key].forEach((subItem, index) => {
test.subproperties.forEach(subprop => {
pm.test(`Property "${test.key}[${index}]" => "${subprop.key}" is of type ${subprop.type}`, function () {
pm.expect(subItem[subprop.key]).to.exist.and.to.be.a(subprop.type, `Property "${test.key}[${index}]" => "${subprop.key}" is missing or not a ${subprop.type}`);
});
});
});
} else {
pm.test(`Property "${test.key}" => "${prop.key}" is of type ${prop.type}`, function () {
if (prop.type === 'null') {
pm.expect(object[prop.key]).to.equal(null, `Property "${test.key}" => "${prop.key}" should be null but is not`);
} else {
pm.expect(object[prop.key]).to.exist.and.to.be.a(prop.type, `Property "${test.key}" => "${prop.key}" is missing or not a ${prop.type}`);
if (prop.value) {
pm.expect(object[prop.key]).to.equal(prop.value, `Property "${test.key}" => "${prop.key}" does not have the expected value ${prop.value}`);
}
}
});
}
});
});
});
Creating a generic validation function for Postman tests can greatly enhance your testing process. It simplifies your test scripts, makes them easier to maintain, and provides clearer error messages when tests fail. By following the steps outlined in this article, you can implement this approach in your Postman test scripts and ensure your API responses are correctly validated.
Lexi Shield: A tech-savvy strategist with a sharp mind for problem-solving, Lexi specializes in data analysis and digital security. Her expertise in navigating complex systems makes her the perfect protector and planner in high-stakes scenarios.
Chen Osipov: A versatile and hands-on field expert, Chen excels in tactical operations and technical gadgetry. With his adaptable skills and practical approach, he is the go-to specialist for on-ground solutions and swift action.
Lexi Shield: A tech-savvy strategist with a sharp mind for problem-solving, Lexi specializes in data analysis and digital security. Her expertise in navigating complex systems makes her the perfect protector and planner in high-stakes scenarios.
Chen Osipov: A versatile and hands-on field expert, Chen excels in tactical operations and technical gadgetry. With his adaptable skills and practical approach, he is the go-to specialist for on-ground solutions and swift action.