Q1 of 40 · REST Assured

How does RequestSpecBuilder reduce duplication in REST Assured tests?

REST AssuredMidrest-assuredrequest-specapi-testingjavatest-design

Short answer

Short answer: RequestSpecBuilder lets you define a reusable base configuration — base URI, auth headers, content type, logging — once, then share it across all tests. Without it, every test repeats the same boilerplate setup, and a base URL change means editing every file.

Detail

REST Assured's default style puts all configuration inline inside a given().when().then() chain, which leads to repeated setup across test classes:

given()
    .baseUri("https://api.example.com")
    .header("Authorization", "Bearer " + token)
    .contentType(ContentType.JSON)
    .when().get("/users/1")
    .then().statusCode(200);

RequestSpecBuilder extracts that repeated preamble into a reusable spec:

RequestSpecification baseSpec = new RequestSpecBuilder()
    .setBaseUri("https://api.example.com")
    .addHeader("Authorization", "Bearer " + token)
    .setContentType(ContentType.JSON)
    .log(LogDetail.ALL)
    .build();

Once built, you pass it to given(baseSpec) in every test. If the base URL or auth scheme changes, you update one place.

Pairing with ResponseSpecBuilder: similarly, define expected response invariants — status code ranges, content-type assertions, max response time — and pass them to .then(baseResponseSpec). The combination produces tests that are short, declarative, and focused on the specific assertion rather than scaffolding.

Initialisation pattern: create the specs in a @BeforeAll method or a BaseTest superclass so they're built once per suite, not once per test method.

// EXAMPLE

BaseApiTest.java

import io.restassured.builder.RequestSpecBuilder;
import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.filter.log.LogDetail;
import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import org.junit.jupiter.api.BeforeAll;

import static org.hamcrest.Matchers.*;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

public abstract class BaseApiTest {
    protected static RequestSpecification reqSpec;
    protected static ResponseSpecification resSpec;

    @BeforeAll
    static void setupSpecs() {
        reqSpec = new RequestSpecBuilder()
            .setBaseUri(System.getenv("API_BASE_URL"))
            .addHeader("Authorization", "Bearer " + getToken())
            .setContentType(ContentType.JSON)
            .log(LogDetail.ALL)
            .build();

        resSpec = new ResponseSpecBuilder()
            .expectStatusCode(anyOf(is(200), is(201)))
            .expectContentType(ContentType.JSON)
            .expectResponseTime(lessThan(2000L), MILLISECONDS)
            .build();
    }
}

// UserApiTest — no boilerplate, focused assertions only
class UserApiTest extends BaseApiTest {
    @Test
    void getUser_returnsCorrectId() {
        given(reqSpec).pathParam("id", 1)
        .when().get("/users/{id}")
        .then().spec(resSpec)
            .body("id", equalTo(1))
            .body("name", not(emptyString()));
    }
}

// WHAT INTERVIEWERS LOOK FOR

Understanding that RequestSpecBuilder solves the DRY problem for API test setup, correct use of both RequestSpecBuilder and ResponseSpecBuilder, and a concrete initialisation pattern (BeforeAll / base class). Strong answers mention the maintenance benefit: single source of truth for auth, base URL, and response invariants.

// COMMON PITFALL

Calling .build() inside each test method, defeating the purpose. The spec should be built once and reused. Another pitfall: not pairing RequestSpecBuilder with ResponseSpecBuilder — they work together to make tests clean.