Interview preparation guide
Postman Interview Questions: Variable Scope and Chained Requests
These Postman interview questions use a fictional reservation collection to make variable resolution visible. You will create a record, capture its identifier, read that exact record and reset the synthetic fixture. Two deliberate failures show why a collection variable can be correct while the request still uses the wrong identifier, and why a failed create must not leave an old identifier available.
Prepare the local fixture
The exercise was executed with Newman 6.2.1, Node 22.22.3 and Python 3.12.14 on macOS arm64. It uses only synthetic records on 127.0.0.1:54942. Save the following as fixture.py, then run python fixture.py in one terminal. Keep the fixture running while you run the collection in another terminal.
This is a small client-workflow exercise. It does not use an account, application credentials or a hosted API. The fixture accepts the morning practice slot; occupied deliberately returns 409. Its reset route clears only its in-memory practice records.
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
records = {}
class Handler(BaseHTTPRequestHandler):
def respond(self, code, body):
payload = json.dumps(body).encode()
self.send_response(code)
self.send_header('Content-Type', 'application/json')
self.send_header('Content-Length', str(len(payload)))
self.end_headers()
self.wfile.write(payload)
def do_POST(self):
if self.path == '/reset':
records.clear()
self.respond(200, {'reset': True})
return
if self.path != '/reservations':
self.respond(404, {'error': 'unknown route'})
return
try:
body = json.loads(self.rfile.read(int(self.headers.get('Content-Length', 0))))
except (ValueError, TypeError):
self.respond(400, {'error': 'invalid JSON'})
return
if body.get('slot') == 'occupied':
self.respond(409, {'error': 'practice slot unavailable'})
return
if body.get('slot') != 'morning':
self.respond(400, {'error': 'use the morning practice slot'})
return
identifier = 'r-' + str(len(records) + 1)
records[identifier] = {'id': identifier, 'slot': body['slot']}
self.respond(201, records[identifier])
def do_GET(self):
identifier = self.path.removeprefix('/reservations/')
if not self.path.startswith('/reservations/') or identifier not in records:
self.respond(404, {'error': 'reservation not found'})
return
self.respond(200, records[identifier])
if __name__ == '__main__':
print('Synthetic reservation fixture: http://127.0.0.1:54942', flush=True)
HTTPServer(('127.0.0.1', 54942), Handler).serve_forever()
Save the complete importable collection
Save this JSON as reservation-practice.postman_collection.json. It uses the Postman collection v2.1 format and was actually executed by Newman, rather than only parsed as JSON. The verified scope is the command-line runner; no Postman desktop import or UI result is claimed.
The requests reset the fixture, create a morning reservation, read its captured ID and clean up. Creation clears the old collection ID before sending, stores a new ID only after a successful response with a nonempty string ID, and stops the collection chain when creation does not yield a usable identifier.
{
"info": {
"name": "Original reservation variable-scope practice",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"variable": [
{
"key": "baseUrl",
"value": "http://127.0.0.1:54942"
},
{
"key": "reservationId",
"value": "old-from-previous-run"
}
],
"item": [
{
"name": "Reset synthetic fixture",
"request": {
"method": "POST",
"header": [],
"url": "{{baseUrl}}/reset"
},
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test('reset succeeded', () => pm.response.to.have.status(200));"
]
}
}
]
},
{
"name": "Create a morning reservation",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"url": "{{baseUrl}}/reservations",
"body": {
"mode": "raw",
"raw": "{\"slot\": \"morning\"}"
}
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"pm.collectionVariables.unset('reservationId');"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test('create returned 201', () => pm.response.to.have.status(201));",
"if (pm.response.code === 201) {",
" const body = pm.response.json();",
" pm.test('creation returned an identifier', () => pm.expect(body.id).to.be.a('string').and.not.empty);",
" if (typeof body.id === 'string' && body.id.length) {",
" pm.collectionVariables.set('reservationId', body.id);",
" }",
"}",
"if (pm.response.code !== 201 || !pm.collectionVariables.has('reservationId')) {",
" pm.execution.setNextRequest(null);",
"}",
"console.log('collection ID after create:', pm.collectionVariables.get('reservationId'));"
]
}
}
]
},
{
"name": "Read the newly created reservation",
"request": {
"method": "GET",
"header": [],
"url": "{{baseUrl}}/reservations/{{reservationId}}"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"const freshId = pm.collectionVariables.get('reservationId');",
"pm.test('a fresh collection ID exists', () => pm.expect(freshId).to.be.a('string').and.not.empty);",
"pm.variables.set('reservationId', freshId);",
"console.log('resolved request ID:', pm.variables.get('reservationId'));"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test('read returned 200', () => pm.response.to.have.status(200));",
"pm.test('read identifies the newly created reservation', () => {",
" pm.expect(pm.response.json()).to.eql({ id: pm.collectionVariables.get('reservationId'), slot: 'morning' });",
"});"
]
}
}
]
},
{
"name": "Clean up synthetic fixture",
"request": {
"method": "POST",
"header": [],
"url": "{{baseUrl}}/reset"
},
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test('reset succeeded', () => pm.response.to.have.status(200));"
]
}
}
]
}
]
}
1. How can the collection ID be correct while the GET request is wrong?
A variable with the same name in a more specific active scope can win when the URL is resolved. In the deliberate failure, creation stored r-1 in collection variables, but the active environment held reservationId=stale-environment-id. Without the read request’s local override, the GET used the stale environment value and returned 404.
Follow-up practice: Which two values would you inspect before changing the server? Compare pm.collectionVariables.get("reservationId") with the value used by pm.variables.get("reservationId").
Save an intentional environment collision
Save this as collision.postman_environment.json. It deliberately creates the conflicting value. There are no secrets in this environment. In a working team collection, consistent variable names and ownership can avoid this collision; here it is retained so the test can prove which value is selected.
{
"name": "Intentional scope collision",
"values": [
{
"key": "reservationId",
"value": "stale-environment-id",
"enabled": true
}
],
"_postman_variable_scope": "environment"
}
2. What does the read request’s local override accomplish?
It reads the freshly captured value from the explicitly chosen collection scope, checks that it exists, and sets a local reservationId for URL resolution. In the tested run this value takes precedence over the conflicting environment value. This is a deliberate scope decision for this request, not a recommendation to conceal every configuration problem with overrides.
Follow-up practice: If the identifier were intended to come from iteration data, how would this explicit override change that design? Describe the chosen source before modifying the script.
Run two complete iterations
Install the pinned runner in a separate practice directory. With fixture.py running, execute these commands from the directory containing the collection and environment files. Both iterations passed; eight requests executed in total, including setup and cleanup for each iteration.
npm init -y
npm install --save-dev newman@6.2.1
npx newman run reservation-practice.postman_collection.json \
-e collision.postman_environment.json \
--iteration-count 2 --timeout-request 2000 --timeout-script 2000
3. Why clear the previous ID before creating a new record?
If a new create fails, leaving an earlier ID available can make later steps inspect the wrong record. The deliberate stale-state test began with old-from-previous-run, changed the create body to {"slot":"occupied"}, and omitted the pre-request unset. Creation returned 409, and the old collection ID remained. An additional assertion that failed creation leaves no reusable ID correctly failed.
Follow-up practice: Why is setting the ID only on successful creation insufficient by itself? Explain what value remains when the success branch never runs.
4. How was the failed-create repair checked?
The same occupied-slot request was rerun with the pre-request unset restored. The expected-201 assertion still failed, accurately reporting unsuccessful creation, but the assertion that no reusable ID remained passed. The chain stopped after creation. This is a safer failure state; it does not turn a 409 response into a successful reservation.
Follow-up practice: What would be misleading about swallowing the status failure because the cleanup assertion passed? Separate application outcome from test-state hygiene.
Reproduce the deliberate failures in copies
Keep the working collection unchanged. For the scope failure, copy it and remove only the Read request’s pre-request script, then run with the collision environment. The observed GET was 404 and both read assertions failed. Restore that script to repair the target ID.
For stale state, make another copy containing only Reset and Create. Change the create body to {"slot":"occupied"}, remove its pre-request unset, and append the assertion below to its post-response script. Run once, then restore the unset and run again. The failed status assertion should remain in both runs; only the stale-ID assertion changes from failed to passed.
pm.test('failed creation leaves no reusable ID', () => {
pm.expect(pm.collectionVariables.has('reservationId')).to.equal(false);
});
5. Why assert the response body as well as status 200?
A successful read can still return the wrong record. This collection compares both the ID with the newly captured collection value and the slot with morning. The assertion ties the response to this creation step instead of treating any readable reservation as sufficient.
Follow-up practice: If the API legitimately adds fields, would an exact-object assertion still express the intended contract? Explain how you would deliberately relax it without losing the ID and slot checks.
6. How would you describe the limits of these results?
Newman executed the actual collection against the synthetic loopback fixture. The two clean iterations passed, the scope collision failed as intended, and the failed-create checks distinguished stale state from cleared state. This does not verify a production booking API, authorization, parallel-user behavior or a Postman desktop workflow.
Follow-up practice: What additional setup and cleanup would a shared test environment require? Do not point this fixture’s reset behavior at a real service.
Rehearse a variable-resolution trace
For each request, state where the value was written, which scope the URL reads, and which result proves it. A useful explanation follows the identifier rather than guessing from a status code.
Observed outcomes and answer repair
Working collection with collision environment: captured r-1 → local r-1 → read200 with expected body; two complete iterations passed.
Read pre-request override removed: collection r-1 exists, but environment value resolves → read404; assertions fail.
Create returns409 with old ID retained: status assertion fails and stale-ID assertion fails.
Create returns409 after clearing old ID: status assertion still fails; no-reusable-ID assertion passes; no dependent read is run.
Weak answer: “A 200 means the collection worked.” Repair: explain which record was read and why its ID belongs to this successful create.
Your trace: writer scope [ ]; URL resolution scope [ ]; previous value [ ]; fresh value [ ]; failure cleanup [ ]; dependent request decision [ ].
Questions, answered
Can I import the collection into Postman?
It is a genuine Postman collection v2.1 JSON file and was run successfully with Newman 6.2.1. Desktop import and execution were not independently checked for this article.
Does the failed-create repair make all assertions pass?
No. The deliberate409 must still fail the expected201 assertion. The repair prevents the earlier ID from surviving and stops the dependent chain.
Why include a reset request at both ends?
Initial reset makes each synthetic iteration start from a known fixture state; final cleanup clears its new record. This fixture-specific design is not a general authorization to delete shared test data.
Sources and examples
ResumizeAI team. Examples are fictional teaching material, not customer results. Replace sample details with your own facts.
Your experience. A complete resume.
Build in Resumize with ATS-friendly templates, guided editing and a live preview. Start free, then upgrade when you need more.
Free account required. Free-plan PDF exports include a watermark.