Postman Advanced Scripting: Dynamic Tests, Pre-Request Scripts, and Data-Driven Testing
Most Postman users write basic tests — pm.response.to.have.status(200) — and call it done. Postman's scripting layer goes much deeper: pre-request scripts that generate dynamic data, test scripts that chain requests, and data-driven testing that runs a collection against hundreds of inputs.
This guide covers the Postman scripting features that turn a collection of requests into a proper test suite.
Understanding Postman's Script Execution Model
Postman runs two types of scripts for each request:
- Pre-request Script: Runs before the request is sent. Use it to generate dynamic data, set variables, compute signatures, or set up test state.
- Tests: Runs after the response is received. Use it to assert response correctness and extract values for subsequent requests.
Scripts run in a JavaScript sandbox with access to pm (the Postman API), pm.environment, pm.globals, pm.collectionVariables, and common libraries (_ for Lodash, CryptoJS, moment, uuid).
Pre-Request Scripts: Dynamic Data Generation
Generating unique identifiers for each run:
// Pre-request script: generate a unique request ID
const uuid = require('uuid');
pm.collectionVariables.set('requestId', uuid.v4());
pm.collectionVariables.set('timestamp', new Date().toISOString());Computing HMAC signatures for authenticated APIs:
// Pre-request script: compute HMAC-SHA256 signature
const secret = pm.environment.get('api_secret');
const timestamp = Math.floor(Date.now() / 1000).toString();
const method = pm.request.method;
const path = pm.request.url.getPath();
const message = `${timestamp}\n${method}\n${path}`;
const signature = CryptoJS.HmacSHA256(message, secret).toString();
pm.request.headers.add({
key: 'X-Request-Timestamp',
value: timestamp,
});
pm.request.headers.add({
key: 'X-Signature',
value: signature,
});Generating test data based on environment:
// Pre-request script: generate environment-appropriate test data
const env = pm.environment.get('environment');
let testUser;
if (env === 'staging') {
testUser = {
email: `test+${Date.now()}@staging.example.com`,
name: 'Staging Test User',
role: 'tester',
};
} else if (env === 'production') {
// Use a dedicated test account in production
testUser = {
email: pm.environment.get('prod_test_email'),
name: pm.environment.get('prod_test_name'),
role: 'viewer', // Minimal permissions for prod tests
};
}
pm.collectionVariables.set('testUser', JSON.stringify(testUser));Request Chaining: Using One Response to Feed the Next
The most powerful use of Postman scripting is chaining requests — using the response from one request to construct the next.
Step 1: Create a resource, capture the ID:
// Tests script for POST /users
const schema = {
type: 'object',
required: ['id', 'email', 'createdAt'],
properties: {
id: { type: 'string' },
email: { type: 'string', format: 'email' },
createdAt: { type: 'string' },
},
};
pm.test('User created successfully', () => {
pm.response.to.have.status(201);
const response = pm.response.json();
pm.expect(tv4.validate(response, schema)).to.be.true;
// Extract and store for subsequent requests
pm.collectionVariables.set('createdUserId', response.id);
pm.collectionVariables.set('createdUserEmail', response.email);
});Step 2: Retrieve the resource using the captured ID:
// URL: {{baseUrl}}/users/{{createdUserId}}
// Tests script for GET /users/:id
pm.test('Returns created user', () => {
pm.response.to.have.status(200);
const user = pm.response.json();
const expectedEmail = pm.collectionVariables.get('createdUserEmail');
pm.expect(user.email).to.equal(expectedEmail);
});Step 3: Delete and verify cleanup:
// DELETE /users/{{createdUserId}}
// Tests script
pm.test('User deleted successfully', () => {
pm.response.to.have.status(204);
});
pm.test('Cleanup: verify user is gone', async () => {
// Use pm.sendRequest to make an inline request and verify
const url = `${pm.environment.get('baseUrl')}/users/${pm.collectionVariables.get('createdUserId')}`;
pm.sendRequest(url, (err, response) => {
pm.expect(response.code).to.equal(404);
pm.collectionVariables.unset('createdUserId');
pm.collectionVariables.unset('createdUserEmail');
});
});Advanced Test Assertions
Schema validation with tv4:
pm.test('Response matches schema', () => {
const schema = {
type: 'object',
required: ['users', 'total', 'page'],
properties: {
users: {
type: 'array',
items: {
type: 'object',
required: ['id', 'email'],
properties: {
id: { type: 'string' },
email: { type: 'string', format: 'email' },
role: { type: 'string', enum: ['admin', 'user', 'viewer'] },
},
},
},
total: { type: 'integer', minimum: 0 },
page: { type: 'integer', minimum: 1 },
},
};
const valid = tv4.validate(pm.response.json(), schema);
pm.expect(valid, tv4.error ? tv4.error.message : 'Schema validation failed').to.be.true;
});Performance assertions:
pm.test('Response time is under 500ms', () => {
pm.expect(pm.response.responseTime).to.be.below(500);
});
pm.test('Response headers include cache control', () => {
pm.expect(pm.response.headers.get('Cache-Control')).to.not.be.empty;
});
pm.test('Content-Type is JSON', () => {
pm.expect(pm.response.headers.get('Content-Type')).to.include('application/json');
});Business logic assertions:
pm.test('Pagination is consistent', () => {
const response = pm.response.json();
const { users, total, page, perPage } = response;
// Verify count consistency
pm.expect(users.length).to.be.at.most(perPage);
// Verify we're not getting more items than total
const expectedItems = Math.min(perPage, total - (page - 1) * perPage);
pm.expect(users.length).to.equal(expectedItems);
});
pm.test('Users are sorted by createdAt descending', () => {
const users = pm.response.json().users;
for (let i = 0; i < users.length - 1; i++) {
const current = new Date(users[i].createdAt).getTime();
const next = new Date(users[i + 1].createdAt).getTime();
pm.expect(current).to.be.at.least(next);
}
});Data-Driven Testing With CSV and JSON
Data-driven testing runs a request multiple times with different inputs. Set it up in the Collection Runner or Newman.
Create a CSV data file:
email,password,expectedStatus,expectedRole
admin@example.com,admin123,200,admin
user@example.com,user123,200,user
invalid@example.com,wrongpassword,401,
notexistent@example.com,whatever,401,Reference data file variables in your request:
// Request body
{
"email": "{{email}}",
"password": "{{password}}"
}
// Tests script
pm.test(`Login with ${pm.iterationData.get('email')}`, () => {
const expected = parseInt(pm.iterationData.get('expectedStatus'));
pm.response.to.have.status(expected);
if (expected === 200) {
const role = pm.response.json().user.role;
pm.expect(role).to.equal(pm.iterationData.get('expectedRole'));
}
});Run with Newman:
newman run collection.json \
--environment staging.json \
--data test-data.csv \
--reporters cli,json \
--reporter-json-export results.jsonCollection-Level Pre-Request Scripts
Scripts at the collection level run before every request in the collection. Use them for authentication:
// Collection pre-request script: auto-refresh auth token
const tokenExpiry = pm.collectionVariables.get('tokenExpiry');
const now = Date.now();
if (!tokenExpiry || now > parseInt(tokenExpiry)) {
const loginUrl = `${pm.environment.get('baseUrl')}/auth/login`;
pm.sendRequest({
url: loginUrl,
method: 'POST',
header: { 'Content-Type': 'application/json' },
body: {
mode: 'raw',
raw: JSON.stringify({
email: pm.environment.get('testEmail'),
password: pm.environment.get('testPassword'),
}),
},
}, (err, response) => {
if (err) {
console.error('Auth failed:', err);
return;
}
const token = response.json().token;
pm.collectionVariables.set('authToken', token);
// Set expiry 55 minutes from now (assuming 60-minute tokens)
pm.collectionVariables.set('tokenExpiry', Date.now() + 55 * 60 * 1000);
});
}Environment-Specific Configuration
Organize environments for different stages:
// staging.json
{
"name": "Staging",
"values": [
{ "key": "baseUrl", "value": "https://api.staging.example.com" },
{ "key": "testEmail", "value": "test@staging.example.com" },
{ "key": "testPassword", "value": "{{STAGING_TEST_PASSWORD}}" },
{ "key": "timeout", "value": "3000" }
]
}Reference environment variables in scripts:
const timeout = parseInt(pm.environment.get('timeout')) || 2000;
pm.test(`Response under ${timeout}ms`, () => {
pm.expect(pm.response.responseTime).to.be.below(timeout);
});Debugging Pre-Request Scripts
Use console.log in Postman scripts — the output appears in the Postman console:
// Pre-request debugging
console.log('Environment:', pm.environment.name);
console.log('Variables:', {
baseUrl: pm.environment.get('baseUrl'),
userId: pm.collectionVariables.get('userId'),
timestamp: new Date().toISOString(),
});Open the console with View → Show Postman Console (⌘+Alt+C on macOS).
Putting It Together: A Complete Test Flow
A well-structured Postman collection for a user management API:
📁 User Management API
├── 📂 Authentication
│ ├── POST /auth/login [sets authToken, userId]
│ └── POST /auth/refresh [updates authToken]
├── 📂 User CRUD
│ ├── POST /users [sets createdUserId]
│ ├── GET /users/:id [uses createdUserId]
│ ├── PATCH /users/:id [verifies update]
│ └── DELETE /users/:id [verifies deletion]
├── 📂 Edge Cases
│ ├── GET /users/nonexistent [expects 404]
│ ├── POST /users (duplicate email) [expects 409]
│ └── PATCH /users/:id (invalid data) [expects 422]
└── 📂 Pagination
├── GET /users?page=1 [verifies pagination]
└── GET /users?page=999 [verifies empty page]With collection-level auth, request chaining via collectionVariables, and data-driven edge case testing, this collection tests the entire API lifecycle in a single run.
HelpMeTest integrates with your existing API tests and adds behavioral end-to-end testing that goes beyond what Postman collections cover. Start free →