Abstract
API testing is one of the fastest ways to verify that a system works the way it should. A well-designed API test does not only check whether a request returns a response; it also validates status codes, response bodies, headers, environment variables, and end-to-end behavior across several requests. In this article, I use Postman to design and organize an API test collection and Newman to run those tests from the command line as part of a repeatable workflow. The example focuses on a small real-world style API for users and tasks, with requests that create data, read it back, and validate the results. I also show how the same collection can be executed locally and inside a CI/CD pipeline. The purpose is to demonstrate that API testing can be practical, readable, and automation-friendly without becoming overly complex.
Introduction
Modern applications depend heavily on APIs. Frontend applications consume them, mobile apps rely on them, and microservices use them to exchange data. Because of that, API testing is no longer optional. If an endpoint returns the wrong status code, exposes an unexpected field, or fails under a basic workflow, the problem can reach users very quickly.
A good API testing strategy should cover more than one request. It should verify that the API accepts the right inputs, returns the correct outputs, and behaves consistently across a sequence of actions. Postman is useful for this because it lets you create requests, add assertions, organize them into collections, and run them in different environments.
Newman complements Postman by making those same collections runnable from the terminal. That is important because once the tests live in a command-line workflow, they can be placed inside CI/CD jobs, scheduled runs, or simple local scripts.
In this article, I will show how to apply both tools to a small API workflow so the process feels realistic instead of theoretical.
Why Postman and Newman
Postman is a strong choice for API testing because it is easy to understand and it supports the full cycle of request creation, test scripting, environment handling, and collection organization. A collection can group related requests, which makes it ideal for scenarios like creating a user, reading the same user back, updating it, and checking the final state.
Newman is the execution layer. It allows the same Postman collection to be run from the command line instead of only inside the app. That means the tests can be automated and repeated in a consistent way. In practice, this is what transforms a manual checklist into a reusable testing workflow.
This combination is attractive because it separates concerns cleanly: Postman is where you design and validate the test logic, and Newman is where you automate it.
The API scenario
For the article, I use a simple fictional API that manages users and tasks. The API exposes a few endpoints:
-
POST /usersto create a user. -
GET /users/{id}to retrieve that user. -
POST /tasksto create a task for the user. -
GET /tasks/{id}to verify task details.
This kind of flow works well because it lets us test both isolated requests and a full sequence. It also reflects what teams often do in real projects: create data, reuse returned values, and assert that the system behaves correctly from one step to the next.
Setting up the collection
A Postman collection can hold all related requests in one place. In this example, the collection could be called User and Task API. It would include an environment with variables such as:
base_url = http://localhost:3000
user_id =
user_name = Kiara
user_email = kiara@example.com
task_id =
task_title = Review API tests
Using variables keeps the requests reusable. If the same collection needs to run in development, staging, or production, only the environment values change.
Request 1: Create a user
The first request creates a user and stores the returned identifier in an environment variable.
POST {{base_url}}/users
Content-Type: application/json
{
"name": "{{user_name}}",
"email": "{{user_email}}"
}
The test script checks that the response is valid and extracts the ID:
pm.test("User created successfully", function () {
pm.response.to.have.status(201);
pm.response.to.be.withBody;
});
const jsonData = pm.response.json();
pm.environment.set("user_id", jsonData.id);
This is a common Postman pattern: send a request, validate the response, and store data for the next step.
Request 2: Read the user
Once the user exists, the second request verifies that the API can retrieve the same resource.
GET {{base_url}}/users/{{user_id}}
A simple test script might look like this:
pm.test("Get user returns 200", function () {
pm.response.to.have.status(200);
});
pm.test("Response contains user email", function () {
const body = pm.response.json();
pm.expect(body.email).to.eql(pm.environment.get("user_email"));
});
This step is important because it checks that the data created in one request can be read back correctly in another request.
Request 3: Create a task
After confirming the user exists, the collection can create a task linked to that user.
POST {{base_url}}/tasks
Content-Type: application/json
{
"title": "{{task_title}}",
"userId": "{{user_id}}"
}
The test script can verify both the response and the business logic:
pm.test("Task created successfully", function () {
pm.response.to.have.status(201);
});
const taskData = pm.response.json();
pm.environment.set("task_id", taskData.id);
pm.expect(taskData.userId).to.eql(pm.environment.get("user_id"));
This is where Postman becomes more than a request sender. It becomes a workflow tester because it can connect one response to the next request.
Request 4: Read the task
The final request confirms that the task was stored correctly and is associated with the right user.
GET {{base_url}}/tasks/{{task_id}}
A validation script may include checks like these:
pm.test("Task returns 200", function () {
pm.response.to.have.status(200);
});
pm.test("Task belongs to the right user", function () {
const body = pm.response.json();
pm.expect(body.userId).to.eql(pm.environment.get("user_id"));
pm.expect(body.title).to.eql(pm.environment.get("task_title"));
});
At this point, the collection has tested an end-to-end flow: create user, read user, create task, and verify task. That is a small but realistic example of how API testing can validate behavior across several endpoints.
Running the collection with Newman
Once the collection works in Postman, the same tests can be executed with Newman from the command line. That is useful when you want to automate the run or include it in a build pipeline.
A typical command looks like this:
newman run user-task-api.postman_collection.json \
-e local.postman_environment.json \
--reporters cli
If the collection and environment are stored in Postman cloud, Newman can also run them through the Postman API in a CI environment.
That matters because it allows the same tests to run every time the code is pushed, instead of depending on someone to open Postman manually.
Newman in CI/CD
Newman becomes especially useful when combined with automation. In a CI/CD pipeline, the test suite can run after code changes, before deployment, or on a schedule.
A simple pipeline could follow this logic:
- Install Node.js and Newman.
- Download or load the Postman collection.
- Load the environment file.
- Run the API tests.
- Fail the pipeline if any assertion fails.
A minimal GitHub Actions example could be:
name: API Tests
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Newman
run: npm install -g newman
- name: Run collection
run: newman run user-task-api.postman_collection.json -e local.postman_environment.json
This turns API testing into a repeatable quality gate rather than a manual afterthought.
What makes this approach practical
The combination of Postman and Newman is practical because it covers both design and execution. Postman makes it easy to model requests and assertions, while Newman makes the same checks runnable from the terminal.
Another advantage is readability. A new team member can inspect the collection and understand the expected flow without needing to read a large testing framework immediately. That is especially helpful in small and medium-sized projects where clarity matters just as much as coverage.
Finally, the approach scales. A collection can start with four requests and later grow into a larger suite with authentication, pagination, error handling, and role-based checks.
Limitations
This approach is helpful, but it is not perfect. Postman and Newman are excellent for API-level validation, but they do not replace every kind of test.
They do not fully cover:
- Performance behavior under heavy load.
- Deep security testing.
- Complex backend logic that requires unit testing.
- Database internals or service implementation details.
For that reason, API tests should be part of a broader testing strategy that includes unit tests, integration tests, and sometimes performance or security checks.
Final thoughts
API testing becomes much more powerful when it moves from manual checking to a repeatable workflow. Postman gives you the structure to define requests, environments, and assertions. Newman gives you the ability to run that work from the command line and automate it in CI/CD.
In practice, that means better consistency, faster feedback, and fewer surprises before deployment. For real-world projects, especially team-based ones, that combination is one of the most accessible ways to build a practical API testing framework.
Top comments (0)