Q11 of 40 · REST Assured

How does ResponseSpecBuilder work? When do you use it over inline assertions?

REST AssuredMidrest-assuredresponse-specapi-testingjavatest-design

Short answer

Short answer: ResponseSpecBuilder defines a reusable response contract — expected status codes, content type, max response time, invariant body fields — built once and shared via .then(responseSpec). Use it for assertions every endpoint must satisfy; keep per-test-specific assertions inline alongside the shared spec.

Detail

Just as RequestSpecBuilder centralises request setup, ResponseSpecBuilder centralises response expectations that apply across many tests:

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

Pass it to .then(baseResSpec) and add per-test assertions on top:

given(reqSpec).when().get("/users/1")
.then()
    .spec(baseResSpec)           // shared invariants
    .body("id", equalTo(1))      // test-specific
    .body("name", equalTo("Alice"));

When to use ResponseSpecBuilder over inline assertions:

  • Invariants shared by every endpoint: content-type, SLA (max response time), envelope fields present
  • Compliance rules: e.g., every response must have a requestId header

When NOT to use it: for assertions that are specific to one test or one endpoint — those belong inline. Over-engineering a ResponseSpecBuilder with 20 assertions creates a brittle shared contract that breaks unrelated tests.

// EXAMPLE

BaseApiTest.java

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

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

        resSpec = new ResponseSpecBuilder()
            .expectContentType(ContentType.JSON)
            .expectResponseTime(lessThan(3000L), MILLISECONDS)
            .build();
    }
}

class UserApiTest extends BaseApiTest {
    @Test
    void getUser_matchesSpec() {
        given(reqSpec).pathParam("id", 1)
        .when().get("/users/{id}")
        .then()
            .spec(resSpec)               // shared contract
            .statusCode(200)             // test-specific
            .body("id", equalTo(1));
    }
}

// WHAT INTERVIEWERS LOOK FOR

Understanding that ResponseSpecBuilder solves the DRY problem for response-side invariants, correct use of .spec() alongside inline assertions, and knowing when NOT to overuse it. Pairing it with RequestSpecBuilder shows the full pattern.

// COMMON PITFALL

Rebuilding the ResponseSpecBuilder inside each test method, defeating its purpose. Build it once in @BeforeAll and share it. Also: stuffing every assertion into the shared spec — shared specs should contain only true invariants.