Q33 of 40 · REST Assured

How would you parallelise REST Assured tests safely? What can break?

REST AssuredSeniorrest-assuredparallel-executionthread-safetyconcurrencyapi-testing

Short answer

Short answer: REST Assured itself is thread-safe when you use local RequestSpecification instances — the thread-safety risk is static RestAssured.* fields. Replace all static fields with local specs or ThreadLocal wrappers. Test data isolation (unique resources per test) is the prerequisite; parallelism is the config layer on top.

Detail

What REST Assured makes thread-safe: the given().when().then() chain is stateless — each call creates new request/response objects. Multiple threads can build and send requests concurrently without interference.

What is NOT thread-safe:

  • RestAssured.baseURI = "..." — static field, shared across threads
  • RestAssured.requestSpecification = ... — static field
  • RestAssured.port — static field

Fix: never use static RestAssured.* assignments; always use given(localSpec) with a locally-built spec.

What can break even with thread-safe code:

  1. Shared test data: two threads create a user with the same email → 409 Conflict. Fix: UUID-prefix every resource.
  2. Shared counters/sequences: tests that rely on the n-th record in the database break when n is non-deterministic in parallel. Fix: assert on specific IDs captured at creation time.
  3. Suite-scope shared mutable state: Wiremock stubs registered globally can interfere. Fix: use per-test stub scoping or @WireMockTest.
  4. Token expiry during a long parallel run: a token fetched at @BeforeAll may expire mid-suite. Fix: use the OAuth2Filter refresh pattern.

// EXAMPLE

ParallelSafeTest.java

// WRONG — static RestAssured fields, NOT thread-safe
static {
    RestAssured.baseURI = "https://api.example.com"; // shared!
    RestAssured.requestSpecification = reqSpec;      // shared!
}

// CORRECT — local spec per test class, built in @BeforeAll
public abstract class BaseApiTest {
    protected static RequestSpecification reqSpec;   // immutable after build

    @BeforeAll
    static void buildSpecs() {
        reqSpec = new RequestSpecBuilder()
            .setBaseUri(System.getenv("API_BASE_URL"))      // local, not static RestAssured.*
            .addHeader("Authorization", "Bearer " + getToken())
            .setContentType(ContentType.JSON)
            .build(); // immutable — safe to share across threads
    }
}

// Each test creates uniquely-named data to avoid collisions
@Test
void createUser_uniqueEmail_noCollisionInParallel() {
    String uniqueEmail = "test-" + UUID.randomUUID() + "@example.com";
    given(reqSpec)
        .body(Map.of("name", "Alice", "email", uniqueEmail))
    .when().post("/users")
    .then().statusCode(201);
}

// WHAT INTERVIEWERS LOOK FOR

The precise distinction between thread-safe (local specs) and non-thread-safe (static RestAssured.*), plus the separate category of data-level thread-safety (unique resources per test). Knowing that the framework is not the only parallelism concern.

// COMMON PITFALL

Assuming that because REST Assured has no synchronized blocks it must be thread-safe everywhere. The static field problem is often discovered only after enabling parallel execution and seeing mysterious failures.