Script examples
Each recipe says where the code goes: a pre-request or post-response script of a request, a folder or the workspace. See Scripts overview for where to edit them.
Check the status and JSON fields
Section titled “Check the status and JSON fields”pm.test("Status is 200", () => { pm.response.to.have.status(200);});
pm.test("Returns the user", () => { const user = pm.response.json(); pm.expect(user).to.have.property("id").that.is.a("number"); pm.expect(user.name).to.be.a("string").and.not.be.empty; pm.expect(user.email).to.match(/^[^@\s]+@[^@\s]+$/); pm.expect(user.roles).to.include("admin");});
pm.test("City is Paris", () => { pm.response.to.have.jsonBody("address.city", "Paris");});Call pm.response.json() inside the test when the body might not be JSON: if it isn’t, only that test fails. Called at the top level of the script, it stops the whole script.
To accept several statuses:
pm.test("Created or already there", () => { pm.expect(pm.response.code).to.be.oneOf([200, 201]);});Save a token for the next requests
Section titled “Save a token for the next requests”Put a login request first, and save the token it returns:
pm.test("Logged in", () => pm.response.to.have.status(200));
const { access_token } = pm.response.json();pm.environment.set("token", access_token);Then use {{token}} in the auth of the folder or workspace, so every request inside gets it. In the folder’s Auth tab choose Bearer and enter {{token}}, or in the file:
name: Ordersauth: type: bearer token: "{{token}}"In the app, the token is kept on this computer for the active environment, never in the environment file. With zorvik run it lasts until the run ends. Declare token as a secret variable in the environment: if its value ever appears in a URL, run reports and history show {{token}} instead.
Chain ids from one request to the next
Section titled “Chain ids from one request to the next”pm.test("Order created", () => pm.response.to.have.status(201));pm.collectionVariables.set("orderId", pm.response.json().id);Later requests use it anywhere a variable works:
GET {{base}}/orders/{{orderId}}DELETE {{base}}/orders/{{orderId}}Which scope to use:
pm.collectionVariablesorpm.environment: the value is still there when you send the next request by hand in the app.pm.variables: the value lasts for one send in the app, or for the whole collection run. Use it for values that only matter inside a run and shouldn’t be kept.
To pass a list, store it as JSON and parse it when reading:
const ids = pm.response.json().items.map((item) => item.id);pm.collectionVariables.set("itemIds", ids); // stored as "[1,2,3]"const [first] = JSON.parse(pm.collectionVariables.get("itemIds") || "[]");pm.variables.set("itemId", first);Check headers
Section titled “Check headers”Header names are compared without case.
pm.test("JSON content type", () => { pm.response.to.have.header("Content-Type"); pm.expect(pm.response.headers.get("content-type")).to.include("application/json");});
pm.test("Not cached", () => { pm.response.to.have.header("Cache-Control", "no-store");});
pm.test("CORS allows our app", () => { pm.expect(pm.response.headers.get("Access-Control-Allow-Origin")).to.be.oneOf(["*", "https://app.example.com"]);});
pm.test("Has a request id", () => { pm.expect(pm.response.headers.has("X-Request-Id")).to.be.true;});headers.get returns the first value of a header. To see every Set-Cookie header:
const cookies = pm.response.headers.filter((h) => h.key.toLowerCase() === "set-cookie").map((h) => h.value);Check response times
Section titled “Check response times”pm.test("Answers within 500 ms", () => { pm.expect(pm.response.responseTime).to.be.below(500);});responseTime is the total time in milliseconds. Zorvik opens a new connection for every request, so it includes DNS, connecting and TLS. For an event stream in a run it’s the time spent reading the stream.
To check every request, put the test in a folder or workspace post-response script, with the limit in a variable:
const limit = Number(pm.variables.get("maxResponseMs") || 1000);pm.test(`Faster than ${limit} ms`, () => { pm.expect(pm.response.responseTime).to.be.below(limit);});Schema checks
Section titled “Schema checks”The shortest way is the jsonSchema assertion:
pm.test("Body matches the schema", () => { pm.response.to.have.jsonSchema({ type: "object", required: ["id", "email"], properties: { id: { type: "integer" }, email: { type: "string", format: "email" } }, });});For more control, use the built-in Ajv yourself:
const Ajv = require("ajv");const ajv = new Ajv({ allErrors: true });const schema = { type: "object", required: ["id", "email"], properties: { id: { type: "integer" }, email: { type: "string", format: "email" }, createdAt: { type: "string", format: "date-time" }, },};
pm.test("Body matches the schema", () => { const valid = ajv.validate(schema, pm.response.json()); pm.expect(valid, ajv.errorsText()).to.be.true;});A failure names every problem: data must have required property 'email', data/id must be integer. tv4.validate(data, schema) works too, for older draft-04 schemas.
Without a schema, checks like these cover most needs:
/** Check that `value` has each key of `shape` with the given type ("string", "number", "array", "object", "boolean", "null"). */function checkShape(value, shape, path) { for (const [key, type] of Object.entries(shape)) { pm.expect(value, path).to.have.property(key); pm.expect(value[key], `${path}.${key}`).to.be.a(type); }}
pm.test("Users have the documented shape", () => { const users = pm.response.json(); pm.expect(users).to.be.an("array").that.is.not.empty; users.forEach((user, i) => { checkShape(user, { id: "number", name: "string", email: "string", roles: "array" }, `$[${i}]`); pm.expect(user.status, `$[${i}].status`).to.be.oneOf(["active", "invited", "disabled"]); });});
pm.test("No unexpected fields", () => { pm.expect(pm.response.json()[0]).to.have.all.keys("id", "name", "email", "roles", "status");});The message argument ($[0].email) tells you which item failed: $[0].email: expected null to be a string.
For a nullable field:
pm.expect(user.deletedAt === null || typeof user.deletedAt === "string", "deletedAt").to.be.true;If the API has an OpenAPI document, import it: Zorvik then checks every response against the documented schemas automatically. See OpenAPI contract checks.
Test Server-Sent Events
Section titled “Test Server-Sent Events”In a collection run, an SSE request is read until the stop condition in its Settings tab (for example, until an event named done), and its post-response script gets the events in pm.response.events.
name: Watch jobkind: sseurl: "{{base}}/jobs/{{jobId}}/events"settings: stream: event: done maxEvents: 100 timeoutMs: 30000scripts: postResponse: | const events = pm.response.events;
pm.test("The job finished", () => { pm.expect(events.length).to.be.above(0); pm.expect(events[events.length - 1].event).to.equal("done"); });
pm.test("Progress never goes backwards", () => { const pct = events.filter((e) => e.event === "progress").map((e) => JSON.parse(e.data).pct); pm.expect(pct).to.eql([...pct].sort((a, b) => a - b)); });Events without an event: line have the name message. See Repeat until and event streams.
Add headers before sending
Section titled “Add headers before sending”pm.request.headers.upsert({ key: "X-Request-Id", value: pm.variables.replaceIn("{{$guid}}") });pm.request.headers.upsert({ key: "X-Sent-At", value: new Date().toISOString() });Basic credentials by hand (the Basic auth type does this for you):
const user = pm.variables.get("user");const password = pm.variables.get("password");pm.request.headers.upsert({ key: "Authorization", value: "Basic " + btoa(`${user}:${password}`) });btoa only encodes Latin-1 text; for any text use CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(text)).
Sign a request with HMAC-SHA256, as many APIs ask, with the built-in crypto-js:
const timestamp = Date.now().toString();const payload = [pm.request.method, pm.request.url.getPathWithQuery(), timestamp, pm.request.body.raw || ""].join("\n");const signature = CryptoJS.HmacSHA256(payload, pm.environment.get("apiSecret")).toString(CryptoJS.enc.Base64);pm.request.headers.upsert({ key: "X-Timestamp", value: timestamp });pm.request.headers.upsert({ key: "X-Signature", value: signature });Change the body before sending
Section titled “Change the body before sending”const body = JSON.parse(pm.request.body.raw || "{}");body.requestedAt = Date.now();body.reference = pm.variables.replaceIn("order-{{$randomInt}}");pm.request.body.raw = JSON.stringify(body);This works for JSON, Text and XML bodies. The change applies to this send only.
Data-driven tests
Section titled “Data-driven tests”With a data file such as:
username,password,expectedStatusada,correct-horse,200bob,wrong,401the request can use {{username}} and {{password}}, and the test can read the expected result:
const expected = Number(pm.iterationData.get("expectedStatus"));pm.test(`${pm.iterationData.get("username")} gets ${expected}`, () => { pm.response.to.have.status(expected);});CSV values are strings, hence Number(…). See Data files.
Control the order of a run
Section titled “Control the order of a run”Run a request again until the server is ready, up to 3 times (the “repeat until” setting is often simpler):
const tries = Number(pm.variables.get("tries") || 0) + 1;if (pm.response.code === 503 && tries < 3) { pm.variables.set("tries", tries); pm.execution.setNextRequest(pm.info.requestName);} else { pm.variables.unset("tries");}Choose the next request from the response:
pm.execution.setNextRequest(pm.response.json().isAdmin ? "Admin dashboard" : "User home");End the iteration early:
if (pm.response.code === 404) { pm.execution.setNextRequest(null); // skip the rest of this iteration}setNextRequest only has an effect in collection runs. See Collection runner.
Skip tests on some environments
Section titled “Skip tests on some environments”const test = pm.environment.name === "Production" ? pm.test.skip : pm.test;
test("Creates a test order", () => { pm.response.to.have.status(201);});Skipped tests show as skipped and don’t fail anything.
Wait for a job to finish
Section titled “Wait for a job to finish”To send a request again until a job is done, don’t loop in a script (there are no timers to wait with). Turn on Repeat until in the request’s Settings tab with a condition such as:
pm.response.json().status === "done"See Repeat until and event streams.
Debug a script
Section titled “Debug a script”console.log("sending", pm.request.method, pm.request.url.toString());console.log("body starts with", pm.response.text().slice(0, 300));console.log("all variables", pm.variables.toObject());Output shows in the response’s Console tab, in expanded results of the Runner, and in results[].console of zorvik run --json.