Introduction
When testing APIs that require authentication or involve dependent requests, hardcoding tokens and dynamic values can quickly lead to fragile and hard-to-maintain tests. PactumJS offers a solution for this – stores, which allow you to capture and reuse values like tokens, IDs, and other response data.
In this article, you’ll learn how to:
- Handle authentication using Pactum stores
- Chain requests by capturing and reusing dynamic values
- Clean up test data using afterEach hooks
Recap: POST Add Room request resulting 401 status code
In the previous article, we created a test case Create a New Room but encountered a 401 Unauthorized error due to missing authentication:
// tests/rooms.spec.js
import pactum from 'pactum';
const { spec, stash } = pactum;
it('POST: Create a New Room', async () => {
await spec()
.post('/room')
.withJson({ '@DATA:TEMPLATE@': 'RandomRoom' })
.expectStatus(200)
.expectJson({
"success": true
})
})
Since the /room endpoint requires authentication, we need to log in and attach a valid session token to our request.
Storing and Reusing Tokens
Pactum allows you to store response values and reuse them across requests using the .stores() method.
To simulate authentication:
await spec()
.post('/auth/login')
.withJson({ '@DATA:TEMPLATE@': 'ExistingUser' })
.stores('token', 'token');
This captures the token field from the login response and stores it under the key ‘token’.
To use the stored token in subsequent requests:
.withHeaders('Cookie', 'token=$S{token}')
Chaining Requests
You can also extract and store specific values like IDs from response bodies using the built-in json-query support in PactumJS. This allows you to query deeply nested JSON data with simple expressions.
For example, to capture a roomId based on a dynamic roomName from the response:
.stores('roomId', `rooms[roomName=${roomName}].roomid`);
Then use it dynamically in future endpoints:
.get('/room/$S{roomId}')
Clean-Up Phase
Cleaning up test data in afterEach ensures that your tests remain isolated and repeatable — a critical practice in CI/CD pipelines.
In this example you can delete all the rooms, which have been created for the test:
afterEach(async () => {
await spec()
.delete('/room/$S{roomId}')
.withHeaders('Cookie', 'token=$S{token}');
});
Full Example: Creating a Room with Authentication
Here’s a full test case demonstrating the use of authentication, value storage, and chaining:
// tests/rooms.spec.js
describe('POST Create a New Room', () => {
beforeEach(async () => {
await spec()
.post('/auth/login')
.withJson({
'@DATA:TEMPLATE@': 'ExistingUser'
}).stores('token', 'token')
});
it('POST: Create a New Room', async () => {
await spec()
.post('/room')
.inspect()
.withHeaders('Cookie', 'token=$S{token}')
.withJson({ '@DATA:TEMPLATE@': 'RandomRoom' })
.expectStatus(200)
.expectJson({
"success": true
})
const roomName = stash.getDataTemplate().RandomRoom.roomName;
await spec()
.get('/room')
.inspect()
.expectStatus(200)
.stores('roomId', `rooms[roomName=${roomName}].roomid`);
await spec()
.get(`/room/$S{roomId}`)
.inspect()
.expectStatus(200)
.expectJson('roomName', roomName);
})
afterEach(async () => {
await spec()
.delete('/room/$S{roomId}')
.inspect()
.withHeaders('Cookie', 'token=$S{token}')
});
})
Understanding the Stash
In the full example above, you may have noticed the use of stash.getDataTemplate():
const roomName = stash.getDataTemplate().RandomRoom.roomName;
The stash object in Pactum provides access to test data and stored values during runtime. Specifically, stash.getDataTemplate() allows you to retrieve values generated from the data template used earlier in .withJson({ ‘@DATA:TEMPLATE@’: ‘RandomRoom’ }).
This is useful here to extract values from dynamically generated templates (like roomName) to use them in later requests.
Bonus: Fetching Rooms without authentication
Here’s a simple test for fetching all rooms without authentication:
// tests/rooms.spec.js
describe('GET: All Rooms', () => {
it('should return all rooms', async () => {
await spec()
.get('/room')
.expectStatus(200);
});
});
Conclusion.
Pactum’s store feature enables you to:
- Authenticate without hardcoding credentials
- Chain requests by dynamically storing and reusing values
By combining this with beforeEach and afterEach hooks, you can effectively manage test preconditions and postconditions, ensuring your test cases remain clean, maintainable.


