DEV Community

muhammad Sanaev
muhammad Sanaev

Posted on

The REST Assured Setup Nobody Shows You: Handling Auth Tokens That Expire Mid-Suite

Most REST Assured tutorials show you a single given().when().then() against a sample API and call it done. That's fine for learning the syntax, but it doesn't cover what you actually need on a real project things like config per environment, clean test structure, and handling auth tokens that expire while the suite is running.

I'm Mukhammadjon Sanaev, a QA Automation Engineer in San Francisco. I've worked across e-commerce, logistics, and sports tech. This post walks through a simple REST Assured + TestNG + Maven setup I'd use on day one of a new API testing project, plus one problem I ran into on a real checkout API that isn't in the tutorials.

What We're Building

A small Java project that:

Uses REST Assured for API calls
Uses TestNG as the test runner
Runs against dev or staging with a single command
Handles an auth token that expires mid-run

Examples are based on an e-commerce checkout API — the kind of thing you'd test at a Shopify- or Wayfair-style company. Nothing proprietary, just the shape of a real checkout flow.

Project Structure
Keep it simple on day one:

api-tests/
├── pom.xml
├── testng.xml
└── src/test/
    ├── java/com/example/tests/
    │   ├── BaseTest.java
    │   ├── TokenManager.java
    │   └── CheckoutTests.java
    └── resources/
        └── config.properties

Enter fullscreen mode Exit fullscreen mode

Step 1: pom.xml

xml<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>api-tests</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>5.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>7.10.2</version>
        </dependency>
    </dependencies>
</project>
Enter fullscreen mode Exit fullscreen mode

Step 2: Config File
Under src/test/resources/config.properties:
properties

base.url=https://api-dev.example.com
auth.url=https://auth-dev.example.com/oauth/token
client.id=your-client-id
client.secret=your-client-secret
Enter fullscreen mode Exit fullscreen mode

Tip: don't commit real secrets to git. In a real project, read client.secret from an environment variable instead.

Step 3: BaseTest
Sets the base URL for every test:

javapublic class BaseTest {
    @BeforeSuite
    public void setup() {
        RestAssured.baseURI = "https://api-dev.example.com";
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: The Auth Problem
Here's the naive way to handle auth, which most tutorials show:
java// Fetch the token once, reuse forever

String token = given()
    .auth().basic("client", "secret")
    .post("/auth/token")
    .jsonPath().getString("access_token");
Enter fullscreen mode Exit fullscreen mode

This works for 10 tests. It breaks when your suite gets bigger.
The Problem
On one project, our API tokens expired after 15 minutes. Our regression suite took about 22 minutes to run. The first batch of tests passed fine, then around test 140 everything started failing with 401 Unauthorized — not because the code was wrong, but because the token had expired halfway through the run.
The fix isn't to make the suite shorter. The fix is making the framework aware that tokens expire.
The Fix: A Simple TokenManager

java

public class TokenManager {
    private static String token;
    private static Instant expiresAt;

    public static String getToken() {
        if (token == null || Instant.now().isAfter(expiresAt)) {
            refresh();
        }
        return token;
    }

    private static void refresh() {
        Response response = given()
            .formParam("grant_type", "client_credentials")
            .formParam("client_id", "your-client-id")
            .formParam("client_secret", "your-client-secret")
            .post("https://auth-dev.example.com/oauth/token");

        token = response.jsonPath().getString("access_token");
        int expiresIn = response.jsonPath().getInt("expires_in");
        // Refresh 60 seconds early to avoid edge cases
        expiresAt = Instant.now().plusSeconds(expiresIn - 60);
    }
}
Enter fullscreen mode Exit fullscreen mode

Two things worth noting:

The 60-second buffer. If you refresh exactly when the token expires, you can still hit a race condition with the server clock. Refreshing a bit early avoids that.
It only refreshes when needed. Most tests just grab the cached token.

Using It
Every API call pulls a fresh token through TokenManager:
javapublic class CheckoutTests extends BaseTest {

    @Test
    public void getCart_returnsItems() {
        given()
            .header("Authorization", "Bearer " + TokenManager.getToken())
            .pathParam("cartId", "cart-123")
        .when()
            .get("/cart/{cartId}")
        .then()
            .statusCode(200)
            .body("items", hasSize(greaterThan(0)));
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Running It
testng.xml:

xml

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="API Tests">
    <test name="Checkout">
        <classes>
            <class name="com.example.tests.CheckoutTests"/>
        </classes>
    </test>
</suite>
Enter fullscreen mode Exit fullscreen mode

Run:
bash

mvn test

That's it a working API test suite that doesn't fall over when tokens expire.
What I'd Add Next
This is a starting point, not the finished framework. Once the basics work, I'd add:

Separate config files for dev, staging, and prod
JSON schema validation on responses
An HTML report like Allure or Extent Reports
CI/CD integration with Jenkins or GitHub Actions

The Takeaway
The tricky parts of API automation aren't the tools REST Assured, TestNG, and Maven are straightforward once you've set them up once. The tricky parts are the problems that only show up when a real suite runs against a real API: auth tokens expiring, environment config drift, response schemas changing silently.
If you're setting this up for the first time, start small. Get one test running, handle auth properly, then grow from there.

Top comments (0)