What does Postman's sandbox actually do with a bare tests["name"] = value assignment in a script?
answer
- It is an object, not an assertion
- Nothing is recorded at that line
- The verdict is just truthiness
- The error message was never really thrown
- One counter serves both spellings
basics
~20 sPostman's sandbox converts the legacy tests object at script end: each key becomes String(key), each value becomes Boolean(value), a falsy one gets a fabricated 'expected <value> to be truthy' error, and the results continue the same test index counter.
solid answer
~40 sA bare `tests[key] = value` is **converted, not merely tolerated**. At the end of the script the sandbox walks the legacy `tests` object and turns every entry into a real result: the name is `String(key)`, the verdict is `Boolean(value)`, and a falsy entry is given a **fabricated** error reading `expected <value> to be truthy` — no assertion library ever evaluated anything, so that message tells you nothing about what was compared. Two consequences matter in an interview. First, the results appear **at script end**, not at the line that wrote them, so a legacy record's position in the report is not its position in the source. Second, they continue the **same `pm.test.index()` counter** as current named blocks, so legacy and current results interleave in one numbered stream rather than forming two separate lists.
code
javascript · 2 linestests["body is not empty"] = pm.response.text().length > 0;
tests["body mentions the account"] = pm.response.text().indexOf("account") !== -1;go deeper
Be ready to say that the legacy tests object is a plain object of name to truthy value, and that everything current is written through named blocks instead. Recognising the spelling matters more than the internals here.
Explain the conversion precisely: keys coerced with String, values with Boolean, a fabricated truthiness error for falsy entries, and all of it produced at script end rather than at the assignment.
Show the diagnostic angle. Point out that truthiness coercion hides checks that have never really failed, and that migrating shifts result positions in the shared index, so a report diff is expected output.
Own the reporting consequence: two spellings feeding one numbered stream means result identity is positional and fragile. Decide whether report consumers should ever depend on that ordering.
## What the assignment becomes In a Postman sandbox script, `tests` is a plain object supplied by the **legacy compatibility layer**. Writing `tests["body is not empty"] = true` is, at the moment you write it, nothing but a property assignment on an ordinary JavaScript object. No result is recorded, no assertion is evaluated, nothing is reported. The interesting behaviour happens later. At the end of the script the sandbox **converts** that object into real results. The conversion is mechanical and total: - the result name is `String(key)` — whatever you used as a key is coerced to a string; - the verdict is `Boolean(value)` — the entry passes if the value is truthy and fails if it is falsy; - a failing entry is given a **fabricated** error whose message reads `expected <value> to be truthy`. This is why the topic is called deprecated globals rather than removed globals: the old spelling is not merely permitted, it is actively translated into the current result shape. ## Before and after, entry by entry | written in the script | recorded at script end | |---|---| | `tests["body is not empty"] = pm.response.text().length > 0` | name `body is not empty`, verdict from `Boolean(...)` | | `tests["has token"] = ""` | a **failing** entry carrying a fabricated `expected to be truthy` error | | `tests[keyVariable] = someValue` | name `String(keyVariable)`, verdict `Boolean(someValue)` | Notice the second row. An empty string is falsy, so the entry fails — and the message you get back is manufactured from the value's truthiness, not from any comparison. Nobody wrote an expectation; the sandbox invented one to have something to report. ## When the results appear, and why it matters The conversion runs **at script end**. That has a practical consequence people trip over when reading a report: 1. A legacy entry's position in the output is not its position in the source. Everything written into `tests` lands together, after the script body has finished. 2. Interleaving a legacy assignment between two current named blocks does not interleave them in the report — the legacy one moves to the end. 3. Because nothing is evaluated at the assignment line, a falsy value does not stop the script. Execution continues to the next statement exactly as any property assignment would. So the mental model "each `tests[...]` line is an assertion that fires there" is wrong on both counts: it is not an assertion, and it does not fire there. ## One numbered stream, not two lists The second half of the mechanism is the numbering. The converted legacy entries **continue the same `pm.test.index()` counter** used by current named blocks. They are not numbered separately, they do not restart at zero, and they are not filed under their own heading. Legacy and current results end up **interleaved in a single numbered stream**. That produces a report that is honest but easy to misread: - a script mixing both spellings yields one continuous sequence of numbered results with no marker saying which spelling produced which entry; - the only structural hint is ordering, and ordering is misleading because the legacy entries were all appended at script end; - the failure messages are the real tell: a fabricated `expected <value> to be truthy` with no comparison detail almost always came from the legacy object. ## Reading and migrating such a script When you meet a script written this way, the useful sequence is: 1. **Identify the entries.** Every `tests[...] = ...` line is a truthiness record, not a test block, whatever it is named. 2. **Look at the expressions, not the names.** Since the value is coerced with `Boolean(...)`, an expression that returns a non-empty string or a non-zero number passes even when it never checked anything meaningful — a common source of tests that have never failed. 3. **Expect the numbering to change.** Rewriting to the current named-block spelling moves those results from the end of the script back to where the code sits, so a before-and-after report diff is expected, not a regression. 4. **Expect the messages to improve.** A fabricated truthiness error is replaced by whatever the rewritten check actually reports, which is the real benefit of migrating rather than a cosmetic one. The one-line answer an interviewer is listening for: a bare `tests[key] = value` is converted at script end into `String(key)` and `Boolean(value)` with an invented truthiness error, and it shares the current index counter — so it is a second spelling feeding one result stream, not a second result system.
- Why can a legacy tests entry pass even though it never really checked anything?Because the verdict is `Boolean(value)`. Any truthy expression passes, so a non-empty string, an object, or a stray non-zero number all record a pass. An entry written as `tests["has id"] = pm.response.text()` passes whenever the body is non-empty, regardless of whether an id is present.
- What changes in the report when you rewrite these entries as current named blocks?Two things move. The results stop appearing at script end and appear where the code sits, so their positions in the shared index change; and the fabricated `expected <value> to be truthy` message is replaced by whatever the rewritten check actually reports. Both differences are expected output, not regressions.
saying these in an interview costs you the question
- Says each tests assignment is evaluated at that line
- Thinks a falsy assignment stops the rest of the script
- Believes the truthiness error came from an assertion library
- Claims legacy results are numbered in their own separate list
- Assumes the legacy object is ignored rather than converted