skip to content

Specification Reuse

Building a RequestSpecification or ResponseSpecification once, attaching it to calls, and undoing it afterwards. Interviewers use it to tell a senior suite from a copy-pasted one.

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

explore

questions

20

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

In REST Assured, which suite-wide defaults do you set as statics on the RestAssured class?

level: juniorimportance: must knowfreq 71%

basics

~20 s

REST Assured keeps suite-wide defaults as public static fields on its RestAssured class: baseURI, port, basePath, authentication, config, requestSpecification, responseSpecification, rootPath, urlEncodingEnabled, defaultParser, sessionId and proxy. Default filters are registered through the static filters method instead.

open as a page

In REST Assured, how do you build one RequestSpecification that every test in a suite reuses?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Chain RequestSpecBuilder setters such as setBaseUri, setBasePath, addHeader and setContentType, then call build() once to obtain a RequestSpecification. Hold that object in a static field so every case starts from it instead of repeating the depot address and headers.

open as a page

In REST Assured, what state does RestAssured.reset() actually put back?

level: middleimportance: must knowfreq 57%

basics

~20 s

RestAssured.reset() restores baseURI, basePath, rootPath, authentication and urlEncodingEnabled to their constants. It nulls requestSpecification, responseSpecification, defaultParser, sessionId and proxy, empties the filter list, drops parser registrations, installs a fresh RestAssuredConfig, and sets port to UNDEFINED_PORT (-1), not 8080.

open as a page

In REST Assured, how do you read a header or base URI back off a built RequestSpecification?

level: middleimportance: must knowfreq 42%

basics

~20 s

REST Assured reads a built request specification back through SpecificationQuerier.query(spec), which returns a QueryableRequestSpecification. That read-only view exposes getters such as getBaseUri, getBasePath, getHeaders, getCookies and getDefinedFilters. No request is sent, so it runs in a plain unit test.

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

Your REST Assured roster suite passes class by class but fails in a full single-threaded run — how do you find the leaked static state?

level: seniorimportance: must knowfreq 49%

basics

~20 s

Reverse or randomise the class order to confirm the failure follows position, not the test. Then print the RestAssured statics at the boundary, find the class that assigned one, and make it call RestAssured.reset() and re-apply the suite defaults.

open as a page

In REST Assured, how do you prove which base URI survived given().baseUri(...).spec(sharedSpec)?

level: seniorimportance: must knowfreq 36%

basics

~20 s

REST Assured can answer that without a server: keep the assembled chain in a variable, pass it to SpecificationQuerier.query, and assert getBaseUri on the QueryableRequestSpecification it returns. The read-back shows exactly which value the merge left behind.

open as a page

In REST Assured, which querier getters show a spec's path placeholders and their values?

level: juniorimportance: should knowfreq 30%

basics

~20 s

REST Assured's QueryableRequestSpecification names every brace in a path with getPathParamPlaceholders and only the unfilled ones with getUndefinedPathParamPlaceholders. The values come back from getPathParams, getNamedPathParams, getUnnamedPathParams and getUnnamedPathParamValues, and getDerivedPath shows the path with known values applied.

open as a page

In REST Assured's RequestSpecBuilder, what is the difference between its addX and setX methods?

level: juniorimportance: should knowfreq 45%

basics

~20 s

RequestSpecBuilder's setX methods fill a single slot, so a second call replaces the first: setBaseUri, setBasePath, setPort, setContentType, setAuth, setBody, setConfig. Its addX methods append to a collection, so repeated calls accumulate: addHeader, addCookie, addQueryParam, addFilter, addMultiPart.

open as a page

In REST Assured, what does a ResponseSpecBuilder let you expect on every response?

level: juniorimportance: should knowfreq 48%

basics

~20 s

ResponseSpecBuilder collects the checks every response must pass — status code, status line, content type, headers, cookies, response time and body paths — and build() returns one ResponseSpecification. It describes expectations only; a real response satisfies them later.

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, why do getRequestParams, getQueryParams and getFormParams return different maps?

level: middleimportance: should knowfreq 34%

basics

~20 s

REST Assured stores parameters in three separate maps, and the querier mirrors them one for one: param feeds getRequestParams, queryParam feeds getQueryParams, formParam feeds getFormParams. A specification never resolves an untyped param, so reading the wrong map returns nothing.

open as a page

In REST Assured, what does RequestSpecBuilder.build() return, and what happens when you call it twice?

level: middleimportance: should knowfreq 36%

basics

~20 s

build() returns the RequestSpecification the builder has been mutating all along, the same object every call, never a copy. Two specs built from one builder are one aliased spec, and later setter calls change a template already handed out.

open as a page

In REST Assured's ResponseSpecBuilder, which expectations stack and which replace an earlier call?

level: middleimportance: should knowfreq 36%

basics

~20 s

A REST Assured ResponseSpecBuilder accumulates body, header and cookie expectations, so repeated calls all run. Status code, status line, content type, response time and log detail are single fields: a second call silently replaces the first. There is no warning.

open as a page

In REST Assured, what does a queried spec's getDefinedFilters() show and what is missing?

level: seniorimportance: should knowfreq 26%

basics

~20 s

getDefinedFilters on REST Assured's QueryableRequestSpecification returns an unmodifiable list of the filters you registered, plus any the specification snapshotted or merged in. The internal filters REST Assured appends at send time, including SendRequestFilter, are not there yet.

open as a page

A REST Assured RequestSpecBuilder ignores the RestAssured.baseURI your setup sets - how do you diagnose and fix it?

level: seniorimportance: should knowfreq 42%

basics

~20 s

new RequestSpecBuilder() copies the RestAssured statics - baseURI, port, basePath, authentication, filters, config, proxy and urlEncodingEnabled - in its constructor, so a builder created before your setup ran keeps the old values. Construct it afterwards, or call setBaseUri directly.

open as a page

A REST Assured ResponseSpecBuilder loses its expectStatusCode(200) after addResponseSpecification(...) — why?

level: seniorimportance: should knowfreq 27%

basics

~20 s

ResponseSpecBuilder.addResponseSpecification runs the response-side merge, which assigns the incoming spec's scalar fields unconditionally. Because that spec never set a status code, its null value is written over your 200. Only body, header and cookie assertions accumulate.

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

In REST Assured, how do RestAssured.filters(...) and replaceFiltersWith(...) differ?

level: middleimportance: nice to knowfreq 33%

basics

~20 s

Both register default filters applied to every subsequent request, but RestAssured.filters(...) appends to the existing private static list, while replaceFiltersWith(...) clears the list first and then adds. The no-argument filters() getter returns an unmodifiable view of it.

open as a page