REST Assured interview question
How do RequestSpecification and ResponseSpecification help you avoid duplication in REST Assured?
Short answer
Build reusable specs with RequestSpecBuilder (base URI, content type, auth, filters) and ResponseSpecBuilder (common status, content type, response-time checks), then apply them with given().spec() and then().spec(). Put them in a base class or client layer so tests only express intent.
Answer
The problem
Without specs, every test repeats the base URI, content type, auth header, logging and the same “200 and JSON” checks. When the auth scheme changes, you edit hundreds of tests.
RequestSpecification
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 static org.hamcrest.Matchers.lessThan;
public final class Specs {
public static RequestSpecification authed(String token) {
return new RequestSpecBuilder()
.setBaseUri(Config.baseUri())
.setBasePath("/v1")
.setContentType(ContentType.JSON)
.setAccept(ContentType.JSON)
.addHeader("Authorization", "Bearer " + token)
.addHeader("X-Request-Id", java.util.UUID.randomUUID().toString())
.log(LogDetail.URI)
.build();
}
public static final ResponseSpecification OK_JSON = new ResponseSpecBuilder()
.expectStatusCode(200)
.expectContentType(ContentType.JSON)
.expectResponseTime(lessThan(3000L))
.build();
private Specs() {}
}
Using them
given()
.spec(Specs.authed(token))
.queryParam("status", "OPEN")
.when()
.get("/orders")
.then()
.spec(Specs.OK_JSON)
.body("items.status", everyItem(equalTo("OPEN")));
Anything set after spec() adds to or overrides the spec for this test, so a negative test can use the same spec with a different header, or skip it and build an unauthenticated request.
Specs can be composed: RequestSpecBuilder.addRequestSpecification(other) merges another spec, and given().spec(a).spec(b) applies both.
Global defaults vs explicit specs
REST Assured has static globals: RestAssured.baseURI, RestAssured.requestSpecification, RestAssured.responseSpecification. They are convenient for small suites but they are static mutable state. In parallel runs, one test setting a global token can leak into another thread. In larger frameworks I prefer building specs explicitly and passing them in, or keeping per-thread state, and only set truly constant values (like a base URI from config) globally, once, before tests start.
Going one step further: a client layer
Specs remove setup duplication; a client layer removes endpoint knowledge from tests:
public class OrdersClient {
private final RequestSpecification spec;
public OrdersClient(RequestSpecification spec) { this.spec = spec; }
public Response create(CreateOrder order) {
return given().spec(spec).body(order).when().post("/orders");
}
public Response get(String id) {
return given().spec(spec).pathParam("id", id).when().get("/orders/{id}");
}
}
Tests then read as intent: orders.create(order).then().statusCode(201). The client returns the raw Response so tests can assert both happy and negative paths; avoid hiding status assertions inside the client, or negative tests become impossible.
What interviewers look for
- Correct use of RequestSpecBuilder and ResponseSpecBuilder with spec().
- Knowing per-test overrides and composition.
- Awareness that static globals are shared mutable state in parallel runs.
- A layered design where tests express intent and specs/clients hold plumbing.
A strong answer covers
- RequestSpecBuilder for shared request setup (base URI, headers, auth, content type)
- ResponseSpecBuilder for common assertions
- Applied with spec() and can be overridden or combined per test
- Global defaults vs explicit specs; thread-safety for parallel runs
- Wraps specs in a client or service layer so tests stay readable