Postman: collections, environments and test scripts
Turn your hand-run API checks into a saved Postman collection with variables, an environment, automatic assertions and a collection run, then run it from the command line with Newman.
In this tutorial you will build a Postman collection for the Brew & Bean API: saved requests, an environment for the base URL, test scripts that check each response automatically, and a one-click run of the whole collection. At the end you will run the same collection from the terminal with Newman, which is how teams run it in CI.
Prerequisites: the practice API from the lesson *Testing APIs by hand* running at http://127.0.0.1:8000, and Postman (the desktop app is free to download; at the time of writing (2026) it has a free plan with some limits, and you may need to create a free account to use all features, so check postman.com for the current terms). If you prefer an open-source tool that works without an account, the next lesson does the same job in Bruno.
Why use an API client
curl is great for one-off checks, but it gets painful when you have 30 requests, several environments (your laptop, a test server) and checks you want to repeat. An API client such as Postman gives you:
- Saved requests grouped in collections, so you do not retype anything. - Variables and environments, so the same request runs against local, staging and production by switching one dropdown. - Test scripts that pass or fail automatically. - A runner that executes the whole collection and shows a report.
Step 1: create a collection and an environment
- 1Open Postman. Click New (or the + beside Collections) and choose Collection. Name it
Brew & Bean API. - 2In the left sidebar open Environments, click +, and name it
Local. - 3Add a variable named
baseUrlwith the initial and current valuehttp://127.0.0.1:8000. Save it. - 4At the top right, pick Local in the environment dropdown. A variable only works when its environment is selected.
- 5Right-click the collection, choose Add request, and name it
Get menu.
Why an environment? Imagine the API moves to a test server at some other address. You change one value (baseUrl) instead of editing 30 requests. Environments are also where you keep secrets like API keys, never hard-coded in requests.
Variables are used with double curly braces: {{baseUrl}}. Postman replaces them when the request is sent.
Step 2: your first saved request
- 1In the
Get menurequest set the method to GET and the URL to{{baseUrl}}/menu. - 2Click Send. You should see status
200 OK, a response time and the JSON list of 8 items. - 3Press Ctrl+S to save the request in the collection.
Step 3: your first test script
Postman runs JavaScript after each response. Open the request's Scripts tab (in older versions it is called Tests) and choose Post-response. Paste this:
pm.test("Status is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Menu has 8 items", function () {
const menu = pm.response.json();
pm.expect(menu).to.be.an("array").with.lengthOf(8);
});
pm.test("Every item has an id, name and a positive price", function () {
pm.response.json().forEach(function (item) {
pm.expect(item).to.have.property("id");
pm.expect(item).to.have.property("name");
pm.expect(item.price).to.be.above(0);
});
});Three checks. pm.test names a check, and pm.expect uses the Chai assertion library that Postman includes.
Click Send again. In the response area open the Test Results tab. You should see three green PASS lines. To see a failure, change lengthOf(8) to lengthOf(9), send again and read the red FAIL message, then change it back.
Each pm.test is one named check. If the assertion inside throws, that check fails, and the others still run. Give checks names that say what should be true, so a failure explains itself.
Step 4: create an order and save its id
Add a second request named Create order: method POST, URL {{baseUrl}}/orders. Open the Body tab, choose raw and JSON in the dropdown, and paste:
{
"customer_name": "Asha",
"phone": "9876543210",
"items": [
{"item_id": "espresso", "qty": 2},
{"item_id": "croissant", "qty": 1}
]
}Now the post-response script. Besides checking the response, it stores the new order's id in a variable so the next request can use it. Chained requests like this are how you test a flow.
pm.test("Order is created", function () {
pm.response.to.have.status(201);
});
const order = pm.response.json();
pm.test("Total is computed by the server", function () {
pm.expect(order.total).to.eql(390);
});
pm.test("New order starts as received", function () {
pm.expect(order.status).to.eql("received");
});
pm.test("Responds in under 500 ms", function () {
pm.expect(pm.response.responseTime).to.be.below(500);
});
pm.environment.set("orderId", order.id);Checks the response and saves order.id as the environment variable orderId.
Send it. Open the Local environment and you will see orderId now has a value such as ord-1001. Each run of the API creates a new id, which is why you save it instead of typing it.
Do not hard-code ids
This API keeps orders in memory and numbers them from ord-1001 each time the server starts. If you restart the server, the old ids vanish. Never hard-code an id like ord-1001 in a later request. Use the saved {{orderId}} variable.
Step 5: read it back, then change its status
Add Get order: GET {{baseUrl}}/orders/{{orderId}} with this script:
pm.test("Order can be read back", function () {
pm.response.to.have.status(200);
});
pm.test("It is the order we created", function () {
const order = pm.response.json();
pm.expect(order.id).to.eql(pm.environment.get("orderId"));
pm.expect(order.customer_name).to.eql("Asha");
pm.expect(order.total).to.eql(390);
});Add Move order to preparing: PATCH {{baseUrl}}/orders/{{orderId}}/status with a raw JSON body {"status": "preparing"} and the script:
pm.test("Status changes to preparing", function () {
pm.response.to.have.status(200);
pm.expect(pm.response.json().status).to.eql("preparing");
});Step 6: add negative tests
Add two more requests that should fail. Their passing test is the failure: it proves the API protects itself.
Create order, bad phone: POST {{baseUrl}}/orders with the same body but "phone": "12345".
Skip a status: PATCH {{baseUrl}}/orders/{{orderId}}/status with {"status": "picked_up"}.
pm.test("Bad phone is rejected with 422", function () {
pm.response.to.have.status(422);
});
pm.test("Error message is helpful", function () {
pm.expect(pm.response.json().detail).to.include("10-digit");
});pm.test("Skipping a status is refused with 409", function () {
pm.response.to.have.status(409);
});Run this one while the order is in preparing: moving straight to picked_up should be refused.
Step 7: run the whole collection
- 1Make sure the requests are in this order: Get menu, Create order, Get order, Move order to preparing, Create order bad phone, Skip a status. Drag them to reorder if needed.
- 2Right-click the collection and choose Run collection (or use the Runner). Check that the
Localenvironment is selected. - 3Click Run. Postman sends each request in order and shows a pass/fail count for every test.
- 4Break something on purpose, such as expecting a total of 400 in
Create order, and run again. Read the failure, fix it and re-run.
Get menu 3 passed
Create order 4 passed
Get order 2 passed
Move order to preparing 1 passed
Create order, bad phone 2 passed
Skip a status 1 passed
Total: 13 passed, 0 failedBecause Create order runs before Get order, the saved orderId flows through. This is the big idea: a collection is a small automated test suite, not just a bookmark list.
Step 8: run it from the command line with Newman
Newman is Postman's command-line runner. It runs a collection without opening the app, so a CI server can do it. First export the collection (the three dots beside the collection, then Export) as brew-bean.postman_collection.json, and the environment (the three dots beside Local, then Export) as local.postman_environment.json. Then, with Node.js installed:
npm install -g newman
newman run brew-bean.postman_collection.json -e local.postman_environment.jsonRuns every request and prints a summary table of requests, assertions and failures. The exit code is non-zero if any test fails, so CI can stop the build.
Keep your exported collection files in Git next to the code. That makes the tests reviewable. The next lesson shows Bruno, which stores requests as plain files by design, making this much smoother.
Good habits in Postman
- Use
{{baseUrl}}everywhere; never type a full URL into a request. - Keep secrets in environment variables, and do not share exported environments that contain real keys.
- Give every request at least a status-code check and one check on the body.
- Name tests as sentences describing the expected behaviour.
- Test negative cases as well as the happy path.
- Keep collections small and focused; organise requests into folders such as Orders and Menu.
Write Postman post-response test scripts (JavaScript using pm.test and pm.expect) for the response below.
Request: [method and URL]
Expected behaviour: [what the API should do, including rules like value ranges]
Sample response:
[paste a real JSON response]
Include: a status check, checks on every field's type and value, a response-time check, and one check that would fail if the response shape changed. Use only the pm API.What to expect: Read every generated assertion before using it. AI often invents field names or checks values that change each run (like ids and timestamps). Run the tests, then break the API on purpose to confirm they actually fail.
Try it: Extend the collection
Add these to your collection, each with tests:
1. Cancel order: create a new order, then PATCH it to cancelled and check the status is cancelled.
2. Cancel a picked-up order: move an order through all stages to picked_up, then try to cancel. Expect 409.
3. Unknown order: GET {{baseUrl}}/orders/does-not-exist and check for 404.
4. Add a collection-level variable qtyLimit set to 10 and use it in a test for the largest allowed quantity.
Key takeaways
- A collection saves requests; an environment holds values like
{{baseUrl}}that change between servers. - Post-response scripts use pm.test and pm.expect to pass or fail each response automatically.
- Save values with pm.environment.set so later requests can use them, instead of hard-coding ids.
- Test negative cases too: the correct 4xx is a pass.
- The collection runner runs everything in order; Newman runs the same collection from the terminal and in CI.
- Store exported collections in Git, and keep secrets out of shared files.
Check yourself
Finishing the quiz marks the lesson as done.
1. What is the main reason to put the server address in an environment variable like {{baseUrl}}?
2. In a Postman script, what does pm.environment.set("orderId", order.id) do?
3. A test named 'Bad phone is rejected with 422' passes when the API returns 422. Why is this a pass and not a failure?
4. What does Newman do?
5. Your 'Get order' request fails with 404 after you restarted the API. What is the most likely cause?
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.