skip to content

Verifying a Reply

What a single check on a reply is actually built out of: a named block that records a verdict, a chain of matchers, and a whole-shape claim. Interviewers probe how you assemble one.

part ofAPI & DB clientsoverview, primer and where to startread it →
on this pageshow

explore

questions

14

In a Postman test script, what does `pm.response.to.have.status(200)` assert, and what else can `status` take?

level: juniorimportance: must knowfreq 82%

answer

  1. One matcher, two kinds of argument
  2. A string is not a code
  3. It compares the phrase, not the number
  4. statusCode and statusReason spell it out
  5. Response.code is numeric, Response.status is text

basics

~20 s

Postman's status matcher is polymorphic: hand it a number and it compares the reply's status code, hand it a string and it compares the reply's reason phrase instead. statusCode and statusReason are the single-purpose spellings.

solid answer

~40 s

`pm.response.to.have.status(200)` asserts the reply's **numeric status code**. The same matcher also accepts a string — `pm.response.to.have.status('Not Found')` — and then compares the reply's **reason phrase**, failing with `expected response to have status reason 'Not Found' but got 'OK'`. The dispatch is on argument type, so a string is never resolved back into a code. Two single-purpose matchers sit beside it: `statusCode(code)` reads only the number, `statusReason(reason)` reads only the phrase, and `statusCodeClass(digit)` compares only the code's leading digit. Everything before the matcher is chai grammar, so `pm.response.to.not.have.status(404)` negates cleanly. Watch the collision with the data property: `pm.response.code` holds the number and `pm.response.status` holds the reason phrase as a string, so `pm.response.status` is not the matcher and never holds 200.

code

javascript · 5 lines
javascript
pm.response.to.have.status(200);
pm.response.to.have.status('OK');
pm.response.to.have.statusCode(201);
pm.response.to.have.statusReason('Created');
pm.response.to.not.have.status(404);

go deeper

for a junior

Recall that status takes either a number or a string, and that a number is the ordinary case. Know that pm.response.code holds the number while pm.response.status holds the reason text.

for a middle

Explain the type dispatch inside status and name the single-purpose matchers behind it: statusCode for the number, statusReason for the phrase, statusCodeClass for the leading digit. Be able to read the failure messages each produces.

for a senior

Show judgment about which part of the status line a suite should depend on. Reason phrases vary between servers and intermediaries, so a suite that asserts them buys itself failures that say nothing about the API's behaviour.

for a principal

Own the convention across many collections: whether teams write status or the explicit statusCode and statusReason, whether class-level checks are allowed, and how that convention keeps assertion failures readable to whoever is on call.

## Where `status` sits on the chain In a Postman test script, `pm.response` is the reply wrapped as the SDK's `Response` object. The sandbox adds a `to` property to that object, and reading it hands back a **chai assertion** whose subject is the response. Everything after `to` is therefore ordinary chai grammar — `have`, `be` and `not` are chai's language chains — while the matchers on the end (`status`, `statusCode`, `statusReason`, `statusCodeClass`, `header`, `body`, `jsonBody`, `responseTime`) are contributed by the `chai-postman` plugin that the sandbox registers into chai before your script runs. `status` is the first link most people ever write: ```javascript pm.response.to.have.status(200); ``` ## `status` dispatches on the argument's type The matcher is **polymorphic**. It inspects what you handed it and behaves differently: - a **number** is read as an HTTP status code and compared with the reply's code; - a **string** is read as a reason phrase and compared with the reply's reason. So `pm.response.to.have.status('Not Found')` is not a mistyped code check — it is a reason-phrase check. Run it against a reply that came back 200 and it fails with `expected response to have status reason 'Not Found' but got 'OK'`. Negate the numeric form instead and the message stays in code language: `pm.response.to.not.have.status(404)` against a 404 fails with `expected response to not have status code 404`. That single detail is what the question is really probing. A candidate who thinks `status` only takes numbers will read a colleague's `status('Created')` as a bug, and a candidate who thinks a phrase is resolved into a code will expect `status('Created')` and `status(201)` to be interchangeable. They are not: one reads the number, the other reads the text. ## The single-purpose spellings `status` is a convenience over matchers that each do one job. In a suite other people read, the explicit spelling is usually kinder. | Matcher | Argument | What it reads off the reply | |---|---|---| | `status(code)` | number | the numeric status code | | `status(reason)` | string | the reason phrase | | `statusCode(code)` | number | the numeric status code only | | `statusReason(reason)` | string | the reason phrase only | | `statusCodeClass(digit)` | number | the leading digit of the code | `statusCodeClass(1)` against a 200 fails with `expected response code to be 1XX but found 200` — it never looks at the other two digits and it never looks at the phrase. ## The two values `status` can compare The code and the phrase are two separate properties on the SDK's `Response`: - `pm.response.code` — a **number**, the status code; - `pm.response.status` — a **string**, the reason phrase. That naming collision catches people. `pm.response.status` is *not* the matcher, and it does not hold a number. A script that writes `pm.expect(pm.response.status).to.eql(200)` compares a string with a number and fails forever. The matcher is reached through `pm.response.to`, never through `pm.response.status`. Where a server sends no reason text of its own, the SDK fills the property in with the standard name for that code, so the phrase is almost always populated — which makes an accidental phrase assertion pass or fail for reasons the author never intended. ## Which one to assert A reason phrase is human-readable text that accompanies the code. It is not something a client should lean on: two servers can answer the same code with different wording, and an intermediary can rewrite it. The code is the stable check; the phrase is a check on wording. 1. **Assert the code**, unless the wording itself is the thing under test. 2. **Prefer `statusCode` or `statusReason`** when a reviewer might otherwise wonder which branch of `status` you meant. 3. **Reach for `statusCodeClass`** when several codes are legitimately acceptable and pinning one would make the test brittle. 4. **Negate with chai's `not`** — `pm.response.to.not.have.status(500)` — rather than inverting the check by hand with an `if`. ## What this link does not cover `status` only ever reads the status line. Headers are the `header` matcher's business, the raw text and the parsed payload belong to `body` and `jsonBody`, and timing belongs to `responseTime`. There is no combined form that checks a code and a header in one call; each link on the chain looks at exactly one part of the reply, and you write one assertion per thing you care about.

  • A colleague writes `pm.response.to.have.status('201')` and the test always fails against a 201 reply. Why?
    The argument is a string, so `status` takes its reason-phrase branch and compares `'201'` with the reply's reason text, which is `Created`. Quoting turned a code check into a phrase check. Drop the quotes for `status(201)`, or say what you mean with `statusCode(201)`.
  • Why is asserting a reason phrase generally weaker than asserting a status code?
    The phrase is human-readable text that servers and intermediaries may word differently for the same code, and the SDK substitutes the standard name when none is sent. The code is the part of the status line clients are meant to branch on, so it is the stable assertion; assert the phrase only when the wording itself is under test.
  • How do you write the same check through `pm.expect` instead of `pm.response.to`?
    `pm.expect(pm.response).to.have.status(200)`. `pm.expect` is chai's `expect`, and `pm.response.to` is a getter that returns `chai.expect(pm.response).to`, so both spellings build the same assertion with the same matchers available on it.

saying these in an interview costs you the question

  • Believes status accepts only a numeric status code
  • Quotes the code, turning a code check into a phrase check
  • Thinks a reason phrase is resolved back into a code
  • Reads pm.response.status expecting a number
  • Claims status also inspects headers or the body
open as a page

In a Postman test script, how do the `body` and `jsonBody` matchers differ in what they read from the reply?

level: middleimportance: must knowfreq 60%

basics

~20 s

Postman's body matcher reads the reply as raw text and takes a string or a regular expression; jsonBody parses the reply first and then takes a path, an expected value, or an object. Both hang off pm.response.to.have.

open as a page

In a Postman test script, what does pm.response.to.have.jsonSchema(schema) assert, and what parses the body?

level: middleimportance: must knowfreq 58%

basics

~10 s

Postman's jsonSchema assertion validates a value against a JSON Schema object using Ajv. Chained off pm.response it parses the reply body first; chained off pm.expect it validates whatever value you handed in.

open as a page

In a Postman script, how many results does one pm.test block containing five pm.expect calls report?

level: middleimportance: must knowfreq 76%

basics

~20 s

Exactly one. A pm.test block is the reporting unit: it emits a single record carrying a name, a passed flag, a skipped flag, an error slot and an index, however many pm.expect calls sit inside it.

open as a page

In a Postman test script, how does a throw inside a pm.test block differ from a throw outside one?

level: middleimportance: must knowfreq 68%

basics

~20 s

Containment. A throw inside pm.test is caught by that block: the block is recorded as failed and the script keeps going. A throw outside any block escapes, ends the script execution and surfaces as an execution error, not an assertion.

open as a page

In a saved Postman collection, what is info.schema, and where does a jsonSchema() assertion's schema come from?

level: juniorimportance: should knowfreq 40%

basics

~10 s

In a collection file, info.schema is a required string naming the collection format the file is written in, not a payload schema. A jsonSchema() assertion takes an object the test script itself builds.

open as a page

What does pm.test.skip do in a Postman script, and what does the block report?

level: juniorimportance: should knowfreq 44%

basics

~20 s

pm.test.skip(name) emits an assertion record for the named block without running any check. The record has skipped set to true, and its passed flag stays true, so a consumer that reads only passed sees a skip as a pass.

open as a page

In a Postman test script, how does `statusCodeClass` differ from asserting an exact code with `status`?

level: middleimportance: should knowfreq 45%

basics

~20 s

Postman's statusCodeClass matcher compares only the leading digit of the reply's status code, so statusCodeClass(2) accepts any code from 200 to 299. The status matcher with a number demands that exact code and nothing else.

open as a page

In Postman's sandbox, what is `pm.response.to` mechanically, and where do its matchers come from?

level: middleimportance: should knowfreq 52%

basics

~10 s

In Postman's sandbox, pm.response.to is a getter that returns chai's expect(this).to for that response. The matchers hanging off it come from the chai-postman plugin the sandbox registers into chai, not from chai's core.

open as a page

You inherit a Postman script calling tv4.validate(...). What is tv4 there, and what replaces it?

level: middleimportance: should knowfreq 42%

basics

~10 s

tv4 is a legacy global the Postman sandbox still exposes and marks deprecated; the sandbox names require('ajv') as its replacement. New scripts assert a payload's shape with the jsonSchema assertion instead.

open as a page

In Postman, why does `pm.response.to.have.responseTime.below(200)` work without calling `responseTime()`?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Read without parentheses, Postman's responseTime matcher retargets the assertion's subject to the recorded duration in milliseconds, so a numeric comparison such as below applies to that number. Invoked as responseTime(), it instead asserts the property is present.

open as a page

A Postman jsonSchema assertion is green on every run. What has it actually proved about the reply?

level: seniorimportance: should knowfreq 34%

basics

~20 s

A green Postman jsonSchema assertion proves only that the reply satisfied the schema object the script itself supplied. That schema is a copy held on your side, so a pass says nothing about the provider's published definition.

open as a page

When does a Postman pm.test block report no result at all, and why would a script hide a check that way?

level: seniorimportance: should knowfreq 36%

basics

~20 s

When the block's function declares a callback parameter and never calls it. That switches pm.test to its asynchronous form, where the record is emitted only by that callback, so an uncalled callback leaves the block absent.

open as a page

In a Postman script, what does pm.test record when its second argument is missing or is not a function?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

A pass. pm.test checks whether the second argument is a function; when it is not, the sandbox emits the block's record immediately with passed true and no error, so a check with no body reports green without testing anything.

open as a page