path

Creating a Generic Validation Function for Postman Tests

Main image

Creating a Generic Validation Function for Postman Tests

Creating a Generic Validation Function for Postman Tests

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.

The Problem with Manual Tests

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.

Introducing the Generic Validation Function

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.

Step-by-Step Guide to Creating the Generic Validation Function

Step 1: Define the Validation Function

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}`);
        }
      });
    }
  });
};

Step 2: Define the Schema

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' }
        ]
      }
    ]
  }
];

Step 3: Use the Validation Function in Your Tests

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');
  });
});

Benefits of the Generic Validation Function

  1. Simplified Test Scripts: By using a generic validation function, your test scripts become cleaner and easier to read.
  2. Ease of Maintenance: Updating your schema or adding new properties is straightforward. You only need to modify the schema definition.
  3. Clearer Error Messages: When a test fails, the error message will include the exact property that caused the failure, making it easier to debug.

Final Function

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}`);
              }
						}
					});
        }
      });
    });
  });

Conclusion

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 & Chen Osipov

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 & Chen Osipov

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.

Published date: 8/5/2024