skip to content

In REST Assured, which calls attach a built specification to a single request or validation?

level: juniorimportance: must knowfreq 70%

answer

  1. three call sites, not a global switch
  2. one on given, one on then
  3. given(req, resp) returns a RequestSender
  4. given(spec) is given().spec(spec)

basics

~20 s

REST Assured attaches a built RequestSpecification with given().spec(reqSpec), and a built ResponseSpecification with then().spec(respSpec). The static given(reqSpec, respSpec) binds both at once, returning a RequestSender on which only an HTTP verb may follow. Each takes effect where you call it.

solid answer

~50 s

There are three attachment points, and every one of them is scoped to a single call rather than to the JVM. - `given().spec(trekSpec)` merges a built `RequestSpecification` into the request you are assembling and returns it, so the chain continues. - `then().spec(bookingCreated)` runs a built `ResponseSpecification`'s expectations against the response you just received; it does not merge into anything. - `RestAssured.given(trekSpec, bookingCreated)` binds both and hands back a `RequestSender` — verbs only, so there is no position after it in which to override anything. The static `given(trekSpec)` is literally `given().spec(trekSpec)`, which puts the spec first in the chain, leaving everything you write afterwards in the winning position. That matters because `spec(...)` merges at the point of the call, and on the request side the incoming spec overwrites scalars such as `baseUri`, `basePath`, `port` and the authentication scheme.

code

java · 15 lines
java
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;

// attach a request spec and a response spec to one call
given().spec(trekSpec)
    .queryParam("region", "andes")
    .pathParam("trekId", "AND-114")
.when()
    .get("/treks/{trekId}/departures")
.then()
    .spec(departuresOk)
    .body("departures[0].llamaCount", equalTo(6));

// or bind both up front - only an HTTP verb may follow
given(trekSpec, bookingCreated).post("/bookings");

go deeper

for a junior

Be ready to name the two everyday call sites out loud: given().spec(reqSpec) for the request and then().spec(respSpec) for the validation, and to say that each affects only that one call.

for a middle

Explain that spec() performs a merge the instant it is called, so where it sits in the chain decides which value survives, and that given(spec) is defined as given().spec(spec).

for a senior

Show the convention you would put in a suite: attach the shared spec first, override afterwards, and keep specs out of static fields so parallel workers cannot interfere with one another.

for a principal

Own the tradeoff between a small number of shared specifications and per-test explicitness. Too few specs and every test drags settings it does not need; too many and nobody can predict what a call actually sends.

## What "attaching" means A `RequestSpecification` is a container of half-built request state: base URI, base path, port, headers, cookies, query and form parameters, filters, an authentication scheme, a body. A `ResponseSpecification` is its mirror on the validation side: an expected status code, an expected content type, header and cookie assertions, body matchers, a body root path. Building one — normally through `RequestSpecBuilder` or `ResponseSpecBuilder` — does nothing on its own. **Attaching** is the act of handing that container to one real call, and REST Assured offers exactly three ways to do it. ## The three attachment points - **`given().spec(trekSpec)`** merges a built `RequestSpecification` into the request you are currently assembling. It returns the same `RequestSpecification`, so the chain continues. - **`then().spec(bookingCreated)`** hands a built `ResponseSpecification` to the validation of the response you have just received. It returns the `ValidatableResponse`, so you can keep asserting. - **`RestAssured.given(trekSpec, bookingCreated)`** binds a request spec and a response spec together and returns a `RequestSender`, which declares only the HTTP verbs — `get`, `post`, `put`, `delete` and the rest. Nothing else can follow it. There is a fourth form that is really the first one wearing a hat: the static `given(trekSpec)` is implemented as `return given().spec(trekSpec);`. That single line tells you exactly where the spec sits in the chain, which turns out to matter a great deal. ## These are per-call attachments, not global state None of the three touches `RestAssured.requestSpecification` or `RestAssured.responseSpecification`. Those static fields are a different mechanism with a different lifetime: they apply to every call in the JVM until something resets them. `spec(...)` applies to the one request or the one validation you are writing, and the next test starts clean. That is what makes it the safe unit of sharing: - A spec attached with `spec(...)` cannot leak into a test that never asked for it. - Two tests in the same class can attach two different specs without fighting each other. - A single edge-case test can attach the shared spec and then override one field — provided the override comes after the attachment. - Nothing has to be torn down afterwards, so parallel workers do not trip over one another. ## Order is part of the meaning `RequestSpecificationImpl.spec(...)` calls `SpecificationMerger.merge(this, incoming)` **at the moment you call it**, and the incoming specification wins on scalar fields. So these two chains against the llama-trekking API are not equivalent: ```java // trekSpec's base URI wins - your call is overwritten given().baseUri("https://api.llamatrek.example").spec(trekSpec).get("/treks"); // your base URI wins - it is applied after the merge given().spec(trekSpec).baseUri("https://api.llamatrek.example").get("/treks"); ``` The habit that follows is simple: **attach first, override afterwards**. Using the static `given(trekSpec)` gets you that for free, because the merge has already happened before you write another character. The response side behaves differently and is worth separating in your head. `then().spec(respSpec)` does not merge into anything — it runs that specification's expectations directly against the response object you already hold, so an assertion you wrote earlier in the same `then()` chain still has to pass as well. ## Which form to reach for | Form | Returns | Good for | |---|---|---| | `given().spec(trekSpec)` | `RequestSpecification` | the default; you still want to add or override things | | `given(trekSpec)` | `RequestSpecification` | identical, but guarantees the spec is first in the chain | | `then().spec(bookingCreated)` | `ValidatableResponse` | a canned block of expectations reused across tests | | `given(trekSpec, bookingCreated)` | `RequestSender` | a fully canned call where only the verb and path vary | A worked call against the booking API, using two of them: ```java given().spec(trekSpec) .pathParam("trekId", "AND-114") .when() .get("/treks/{trekId}/departures") .then() .spec(departuresOk) .body("departures[0].llamaCount", equalTo(6)); ``` ## The mistakes people actually make - Believing `spec(...)` is a fallback layer that only fills in blanks. It is a merge, and on the request side the scalars are copied straight over. - Expecting `given(trekSpec, bookingCreated)` to leave room for a `queryParam(...)`. It hands back a `RequestSender`, so the only thing you can do next is send. - Reaching for `RestAssured.requestSpecification` when a per-call `spec(...)` would do. The static field is process-wide state and has to be reset. - Assuming a spec built with `new RequestSpecBuilder()` and nothing set on it is inert. Its constructor snapshots the `RestAssured` statics, so it always carries a base URI and a scheme. Get the three call sites and the "attach first, override afterwards" habit straight, and the rest of specification reuse is bookkeeping.

  • If given(trekSpec, bookingCreated) returns a RequestSender, how do you add one query parameter for a single test?
    You cannot, from that form — `RequestSender` declares only the HTTP verbs. Fall back to `given().spec(trekSpec).queryParam("region", "andes")` and attach the response spec separately with `then().spec(bookingCreated)`. The two-argument form is for calls where nothing but the verb and path vary.
  • How does attaching a spec with given().spec(...) differ from assigning RestAssured.requestSpecification?
    `given().spec(...)` affects one request and nothing else. `RestAssured.requestSpecification` is a static field that is merged into every request in the JVM until it is reassigned or `RestAssured.reset()` is called, so it is shared mutable state that has to be torn down and is unsafe to change while other tests run.

saying these in an interview costs you the question

  • Thinking spec() has to be the last call in the chain
  • Calling spec() a global setting rather than a per-call merge
  • Expecting to chain queryParam() after given(req, resp)
  • Believing then().spec() replaces assertions written earlier
  • Assuming a spec must be attached before every request to work