Postman Advanced Scripting: Dynamic Tests, Pre-Request Scripts, and Data-Driven Testing

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.json

Collection-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 →

Read more

Start now free