skip to content

Attaching to a Call

Handing a specification to one request or one validation, and the merge underneath: some settings the incoming spec overwrites, others it adds to. The order you call it in changes the result.

part ofREST Assuredoverview, primer and where to startread it →
on this pageshow

questions

4

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
open as a page

Your REST Assured booking test sets baseUri and auth, then calls spec(trekSpec), and both are ignored — why?

level: seniorimportance: must knowfreq 58%

basics

~20 s

REST Assured's spec(...) merges at the point of the call, and the incoming specification wins on scalars: SpecificationMerger overwrites baseUri, basePath, port and the authentication scheme. Move spec(trekSpec) to the front of the chain. Precedence is call order, not request-beats-spec.

open as a page

In REST Assured, which settings does given().spec(...) overwrite and which does it accumulate?

level: middleimportance: should knowfreq 50%

basics

~20 s

SpecificationMerger overwrites the scalar request settings — port, baseUri, basePath, authentication scheme, request body, urlEncodingEnabled, proxy, method and path — while it accumulates the collections: parameters, named path parameters, multiparts, cookies, headers and filters. Duplicate filter instances are added once.

open as a page

In REST Assured, how does then().spec(respSpec) differ from expect().spec(respSpec)?

level: middleimportance: nice to knowfreq 32%

basics

~20 s

then().spec(respSpec) does not merge: ValidatableResponseOptionsImpl runs that specification's expectations directly against the response already received, so earlier assertions in the chain still apply. expect().spec(respSpec) merges through SpecificationMerger, where the incoming spec overwrites the expected status code.

open as a page