When does a Postman pm.test block report no result at all, and why would a script hide a check that way?
answer
- The declared parameter count decides the mode
- Async blocks report from their callback
- An uncalled callback emits nothing
- Absent is not the same as passed
- Never declare a parameter you ignore
basics
~20 sWhen 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.
solid answer
~40 s`pm.test(name, fn)` decides synchronous versus asynchronous from `fn`'s **declared parameter count**, not from the `async` keyword. With zero parameters the sandbox runs `fn`, catches any throw, and emits the record immediately. With one or more parameters it passes a completion callback in and marks the record `async: true`; the record is emitted only when your code invokes that callback — passing an error fails the block, passing nothing passes it. If the callback is never called, nothing is dispatched: the block produces **no** record, no failure, and no error. A check written this way disappears silently, which is worse than failing, because a report cannot show what it never received.
code
javascript · 5 linespm.test('token refresh completes', function (done) {
setTimeout(function () {
done();
}, 10);
});go deeper
Recall that a check that finishes later needs the callback form of pm.test, and that you must actually call the callback for the block to report anything.
Explain that the declared parameter count, not the async keyword, chooses the mode, and that in the async form the record is emitted only from the callback.
Recognise a suite that quietly stopped reporting a check, trace it to a declared-but-uncalled callback, and argue why an absent block is more dangerous than a failing one.
Own the guard against silent shrinkage: a convention that synchronous blocks declare no parameters, and a way for the team to notice when the number of reported blocks drops.
## The parameter count is the switch `pm.test(name, fn)` inspects how many parameters `fn` declares and takes one of two paths. With **no declared parameters**, the block is synchronous. The sandbox calls `fn()` inside a try/catch, marks the record failed if anything throws, and emits it immediately on the next line. With **one or more declared parameters**, the block is asynchronous. The sandbox sets `async: true` on the record and calls `fn(done)`, handing in a completion callback. Now the record is emitted from inside that callback and nowhere else. Your code owns when the verdict is reported. This is worth stating precisely because it surprises people: the switch is the *shape of the function signature*. Declaring a parameter you then ignore is enough to flip a block into async mode. | | synchronous form | asynchronous form | |---|---|---| | trigger | `fn` declares no parameters | `fn` declares at least one | | `async` field | `false` | `true` | | record emitted | right after `fn` returns | when the callback is invoked | | a throw in the body | caught, block failed | caught, block failed | | callback given an error | n/a | block failed, error stored | | callback never invoked | n/a | **no record at all** | ## The silent-disappearance case The last row is the interesting one. If an async block's callback is never invoked, the sandbox has nothing to emit. No assertion record is dispatched, and no execution error is raised either — the script simply finishes. The block is not passed, not failed, not skipped. It is **absent**. The ways this happens in practice all look reasonable on the page: - a callback parameter is declared out of habit, the body is written synchronously, and nothing ever calls it; - the callback is called only on the success branch of a conditional, so the failure path emits nothing; - the callback is called inside a handler that never fires; - an error is thrown on the path that would have led to the callback, after the callback was already committed to. In each case the run looks *healthier* than the truth: one fewer red line, one fewer line altogether. ## Why absence is worse than failure A failed block is evidence. It has a name, an index, an error, and it lands in the failure roll-up where somebody reads it. An absent block is nothing at all, and nothing is indistinguishable from a check that was never written. Anyone auditing the run sees a set of green lines and no reason to suspect one is missing — the count of blocks is not a fixed number anyone compares against. That asymmetry is why this belongs to production judgment rather than trivia. The rule that falls out of it: 1. **Do not declare a parameter you are not going to call.** If the body is synchronous, take no parameters. 2. **When the block genuinely is asynchronous, call the callback on every path** — success, failure, and the error branch — the way you would with any completion-style API. 3. **Prefer the throw over the error argument inside a synchronous block.** A throw is caught for you and stored in the record; there is nothing to forget. 4. **Treat a shrinking number of reported blocks as a defect**, not as an improvement. ## One more detail about the async form A synchronous throw inside an async block is still caught: the record is marked failed and emitted immediately, and a later invocation of the callback does not overwrite that verdict — the sandbox checks whether a synchronous error already failed the block and returns without re-reporting. So the block is reported once even when both things happen. That guard exists because the two channels — throwing, and calling the callback with an error — are both live in an async block, and a record may only be emitted once. It is a good illustration of the general shape of `pm.test`: whatever the body does, exactly one verdict is meant to leave the block. The failure mode described above is the one case where the count comes out as zero instead of one.
- How does an asynchronous pm.test block report a failure?By invoking its callback with an error, for example `done(new Error('...'))`. That marks the record `passed: false` and stores the error, exactly as a throw would in the synchronous form. Invoking the callback with no argument reports a pass. A throw in the body is also still caught and fails the block.
- A block throws synchronously and its callback is later invoked with an error too. How many records appear?One. The synchronous throw is caught, the record is marked failed and emitted straight away. The sandbox then checks, when the callback runs, whether a synchronous error already failed the block and returns without emitting again. Exactly one verdict leaves the block, which is the invariant the whole design maintains.
Declaring the callback is telling the runner "wait, I will phone in my result". Never phoning does not fail the block — it leaves the runner with an empty line where a verdict belonged.
saying these in an interview costs you the question
- Thinks the async keyword decides the mode
- Declares a done parameter in a synchronous block
- Assumes an uncalled callback fails or times out the block
- Calls the callback only on the success branch
- Reads a missing block as a passing one