PactumJS in Practice: Using Data Templates to Manage Test Data – Part 1

Introduction

In this hands-on guide, we’ll explore how to improve the maintainability and flexibility of your API tests using data templates in PactumJS. Our focus will be on the authentication endpoint: POST /auth/login

Recap: A Basic Login Test

In the previous article we wrote a basic test case for a successful login:

it('should succeed with valid credentials', async () => {
  await spec()
    .post('/auth/login')
    .inspect()
    .withJson({
      username: process.env.USERNAME,
      password: process.env.PASSWORD,
    })
    .expectStatus(200);
});

While this works for one case, hardcoding test data like this can quickly become difficult to manage as your test suite grows.

Improving Test Maintainability with Data Templates

To make our tests more scalable and easier to manage, we’ll introduce data templates — a PactumJS feature that allows you to centralize and reuse test data for different scenarios, such as valid and invalid logins.

Step 1: Define Auth Templates

Create a file auth.js inside your templates directory /helpers/datafactory/templates/ and register your authentication templates:

// helpers/datafactory/templates/auth.js

import pkg from 'pactum';
const { stash } = pkg;
import { faker } from '@faker-js/faker/locale/en';
import dotenv from 'dotenv';
dotenv.config();

export function registerAuthTemplates() {
  stash.addDataTemplate({
    ExistingUser: {
        username: process.env.USERNAME,
        password: process.env.PASSWORD,
    },
    NonExistingUser: {
        username: 'non-existing-user',
        password: 'password',
    }
});
}

Step 2: Register All Templates in a Central File

Next, create a registerDataTemplates.js file to consolidate all your template registrations:

//helpers/datafactory/templates/registerDataTemplates.js
import { registerAuthTemplates } from "./auth.js";

export function registerAllDataTemplates() {
    registerAuthTemplates();
    registerRoomTemplates();
  }

Step 3: Use Templates in Your Test Setup

Finally, import and register all templates in your test suite’s base configuration:

// tests/base.js

import pactum from 'pactum';
import dotenv from 'dotenv';
dotenv.config();
import { registerAllDataTemplates } from '../helpers/datafactory/templates/registerDataTemplates.js';

const { request } = pactum;

before(() => {
  request.setBaseUrl(process.env.BASE_URL);
  registerAllDataTemplates()
});

Writing Login Tests with Templates

Now let’s implement test cases for three core scenarios:

// tests/auth.test.js

describe('/auth/login', () => {

  it('should succeed with valid credentials', async () => {
    await spec()
      .post('/auth/login')
      .withJson({ '@DATA:TEMPLATE@': 'ExistingUser' })
      .expectStatus(200)
      .expectJsonSchema(authenticationSchema);
  });

  it('should fail with non-existing user', async () => {
    await spec()
      .post('/auth/login')
      .withJson({ '@DATA:TEMPLATE@': 'NonExistingUser' })
      .expectStatus(401)
      .expectJsonMatch('error', 'Invalid credentials');
  });

  it('should fail with invalid password', async () => {
    await spec()
      .post('/auth/login')
      .withJson({
        '@DATA:TEMPLATE@': 'ExistingUser',
        '@OVERRIDES@': {
          password: faker.internet.password(),
        },
      })
      .expectStatus(401)
      .expectJsonMatch('error', 'Invalid credentials');
  });

});

💡 Did You Know?

You can use:

  • @OVERRIDES@ to override fields in your template (e.g. testing invalid passwords)
  • @REMOVES@ to remove fields from the payload (e.g. simulating missing inputs)

Example:

it('should return 400 when username is missing', async () => {
  await spec()
    .post('/auth/login')
    .withJson({
      '@DATA:TEMPLATE@': 'ExistingUser',
      '@REMOVES@': ['username']
    })
    .expectStatus(400);
});

Conclusion

Data templates in PactumJS are a simple yet powerful way to make your API tests more maintainable and scalable. By centralizing test data, you reduce duplication, improve readability, and make your test suite easier to evolve as your API grows.

In this part, we focused on authentication. In the next article, we’ll explore how to apply the same pattern to other endpoints — like POST /room — and build more complex test scenarios using nested data and dynamic generation.

Comments

Leave a comment