On this page10 sections

API Assertions & Test Scripts

Write Postman pm.test assertions for status codes, response body fields, JSON schema validation, and response time — then run the full collection in the Collection Runner and produce a pass/fail summary.

Role

Manual QA engineer

Difficulty

Intermediate

Time limit

~90 min

Category

api client

Postman (free desktop app at postman.com/downloads or web at app.getpostman.com)

Scenario

Your team has built a Postman collection for the Reqres.in API and now needs to convert it from a manual exploration tool into a repeatable test suite. Rather than visually inspecting responses after each run, you will add pm.test assertions to every request so failures are caught automatically. You will also add a JSON Schema validation check to the GET /api/users response to catch structural regressions, set a response-time threshold, and run the full collection in the Postman Collection Runner to produce a pass/fail result that can be reviewed at the end of a test session.

Requirements

  • 1.Add status-code assertions to every request using pm.test and pm.response.to.have.status(); each assertion must include a descriptive test name — 'Status is 200' is acceptable but 'GET /users returns 200 OK' is better
  • 2.Add response body assertions to at least 5 requests checking: (a) a required field exists and has the correct type (e.g. pm.expect(pm.response.json().data.id).to.be.a('number')); (b) an array field has the expected length or is non-empty; (c) a specific expected value is present (e.g. the error message on a 400 response matches 'Missing password')
  • 3.Add a JSON Schema validation test to the GET /api/users?page=2 request: define an inline schema object covering the top-level fields (data array, page integer, per_page integer, total integer, total_pages integer) and use pm.expect(tv4.validate(pm.response.json(), schema)).to.be.true — or use the Ajv-based approach with const Ajv = require('ajv'); if your Postman version supports it
  • 4.Add a response-time assertion to every request: pm.test('Response time is under 2000ms', () => pm.expect(pm.response.responseTime).to.be.below(2000)) — note any request that regularly exceeds the threshold without failing the assertion
  • 5.Intentionally introduce one assertion failure: for example, assert pm.response.to.have.status(201) on a GET request that returns 200, run the collection, observe the failure in the runner, then fix it and run again to confirm all tests pass
  • 6.Run the complete collection in the Collection Runner (without a data file) and capture the summary: total requests, passed tests, failed tests; include a screenshot or a text copy of the runner results

Starter data

  • Start from the collection built in postman-collection-basics or postman-environments-variables; all assertions go in the Tests tab of each request
  • pm.test syntax: pm.test('test name', function() { pm.expect(actual).to.equal(expected); }); — pm.expect uses the Chai BDD assertion library; available matchers include .equal, .be.a, .include, .have.property, .be.above, .be.below, .be.empty, .not.be.empty
  • Reqres.in /api/users?page=2 response shape: { data: Array<{id, email, first_name, last_name, avatar}>, page: 2, per_page: 6, total: 12, total_pages: 2, support: {url, text} }
  • Minimal JSON Schema for the users-list response: { type:'object', required:['data','page','per_page','total','total_pages'], properties: { data:{type:'array'}, page:{type:'integer'}, per_page:{type:'integer'}, total:{type:'integer'}, total_pages:{type:'integer'} } }
  • tv4 library: available globally in Postman sandbox — const result = tv4.validate(pm.response.json(), schema); pm.test('Schema is valid', () => pm.expect(result).to.be.true);
  • Collection Runner: in Postman desktop, click the collection name → Run — opens the runner; keep 'Delay' at 0ms for Reqres.in; all requests run in folder order; the summary shows individual test pass/fail per request

Expected deliverables

  • Updated collection export with pm.test assertions added to every request's Tests tab
  • Status-code assertions on all requests
  • Body field assertions on at least 5 requests covering field existence, type checking, and a specific value check
  • JSON Schema validation test on GET /api/users?page=2
  • Response-time assertion on all requests (threshold: 2000ms)
  • Collection Runner results: screenshot or text copy showing total requests, passed tests, and failed tests — with zero failures after the intentional failure has been fixed

Evaluation rubric

DimensionWhat reviewers look for
Assertion coverageDo assertions go beyond status codes to verify the response body? Status-code-only assertions catch network or server errors but miss business-logic failures (e.g. the users endpoint returning an empty data array with a 200 status). Body assertions for field existence, type, and value catch the actual contract. A collection with only status-code assertions is not a test suite — it is a health check.
Meaningful test namesDo test names describe what is being asserted, not just the mechanism? 'Status is 200' tells a reviewer nothing about why 200 is expected here. 'POST /api/users returns 201 Created' or 'login failure returns 400 with error message' are meaningful and make the runner summary readable without opening each test. A runner output of 15 × 'Status is 200' cannot be triaged without opening every request.
Schema validation correctnessDoes the JSON Schema assertion actually fail when the response structure changes? A schema with no required fields and no type constraints will pass for any object including an empty one. The schema must specify required field names and their types at minimum. Test this by temporarily adding a misspelled field name to the required array and confirming the assertion fails — then restore it.
Response-time threshold relevanceIs the 2000ms threshold appropriate for a public demo API, and is any consistently slow request noted? A threshold of 100ms would produce flaky failures on a public API over the internet. 2000ms is a realistic public-API ceiling. If a request regularly responds in 1800ms, that is a documented observation worth noting, even if it never crosses the threshold.
Runner usage and result interpretationDoes the runner summary correctly show which tests passed and failed? Can you identify from the runner output alone which request and which specific test assertion failed — without clicking into each request? A runner result that requires manual navigation to identify a failure is not a test run summary; it is a list of requests. Test names must be descriptive enough to make failures self-explanatory.

Sample solution outline

  • GET /api/users?page=2 Tests: pm.test('Status 200', () => pm.response.to.have.status(200)); pm.test('data array has 6 items', () => pm.expect(pm.response.json().data).to.have.lengthOf(6)); pm.test('page is 2', () => pm.expect(pm.response.json().page).to.equal(2)); pm.test('Schema valid', () => pm.expect(tv4.validate(pm.response.json(), schema)).to.be.true);
  • GET /api/users/2 Tests: status 200; pm.test('User id is 2', () => pm.expect(pm.response.json().data.id).to.equal(2)); pm.test('email is a string', () => pm.expect(pm.response.json().data.email).to.be.a('string'));
  • GET /api/users/23 Tests: pm.test('Status 404 for non-existent user', () => pm.response.to.have.status(404)); pm.test('Body is empty object', () => pm.expect(pm.response.json()).to.deep.equal({}));
  • POST /api/users Tests: pm.test('Status 201 Created', () => pm.response.to.have.status(201)); pm.test('Response contains id', () => pm.expect(pm.response.json()).to.have.property('id')); pm.test('createdAt is an ISO string', () => pm.expect(pm.response.json().createdAt).to.match(/^\d{4}-\d{2}-\d{2}/));
  • POST /api/login (failure) Tests: pm.test('Status 400', () => pm.response.to.have.status(400)); pm.test('Error is Missing password', () => pm.expect(pm.response.json().error).to.equal('Missing password'));
  • Runner summary (expected): 8 requests, 26 tests passed, 0 failed; response times all under 800ms on a low-latency connection; the intentional 201-on-GET failure showed as 'GET /api/users — list | Status 201 Created | FAIL' and was corrected by changing the expected status back to 200

Common mistakes

  • Writing assertions outside pm.test() wrappers — pm.expect() outside a pm.test() callback throws an uncaught error that fails the entire request instead of recording a named test failure; all assertions must be inside pm.test('name', function() { ... })
  • Asserting status 200 on POST requests — POST /api/users correctly returns 201 Created; asserting 200 means the test passes even if the API is incorrectly configured to return 200 instead of 201, hiding the semantic error
  • Defining the JSON Schema inside the pm.test callback on every run — the schema object should be defined once before the test block using const schema = { ... }; defining it inside the assertion function works but is harder to read and harder to update
  • Using pm.response.text() when you need pm.response.json() — pm.response.text() returns the raw response body as a string; comparing a string to a JSON object will always fail; use pm.response.json() to get the parsed object when writing body assertions
  • Treating a green runner result as proof that the API is correct — Postman only asserts what you have written; if you have not asserted that the data array contains the correct user IDs, the runner will pass even if the IDs are wrong; test coverage is only as good as the assertions written
  • Not running the collection in the runner to produce a summary — manually clicking through requests and visually checking pass/fail is not a test run; the runner produces a repeatable, time-stamped result; the task requires a runner summary, not individual request screenshots

Submission checklist

  • Updated collection export with Tests-tab scripts on every request
  • Status-code assertions on all requests with descriptive test names
  • Body assertions on at least 5 requests: field existence, type check, and specific-value check
  • JSON Schema validation test on GET /api/users?page=2
  • Response-time assertion (below 2000ms) on all requests
  • Collection Runner summary showing total requests, passed tests, failed tests — with zero failures
  • Note on the intentional failure: which assertion was wrong, what the runner showed, and how it was fixed

Extension ideas

  • +Add a test to POST /api/login (success) that stores the token in an environment variable and then add a test to a subsequent GET request that asserts the Authorization header was sent — this combines assertions with the token-chaining pattern from postman-environments-variables
  • +Write a negative schema test: duplicate the GET /api/users Schema validation test, add a required field that does not exist in the response (e.g. 'nonExistentField'), run it, confirm the test fails, then document what a schema failure message looks like in the runner
  • +Export the runner results as a JSON file (Postman runner → Export Results) and write a 3-sentence summary interpreting the result as you would for a team stand-up: what passed, what failed, and what the next action is