In REST Assured, what happens to a then().statusCode(201).extract().path(...) chain when the status check fails?
answer
- assertions fire as you call them
- nothing is queued for the end
- the chain throws before extract()
- AssertionError at the failing expectation
- expectations above extract(), not below
basics
~20 sNothing after the failing check runs. REST Assured validates each then() expectation the moment you call it, so a failed status assertion throws an AssertionError there and then; extract() is never reached and your local variable is never assigned.
solid answer
~40 sREST Assured's `then()` chain validates **eagerly**. By the time you call `then()` the response already exists, so each expectation — `statusCode(201)`, `body(...)`, `header(...)` — is checked inside that method call rather than queued up. The deferred form belongs to the older `expect()`-before-the-request style, where no response is attached yet. So in `.then().statusCode(201).extract().path("fittingId")`, a 500 from `POST /fittings` throws `java.lang.AssertionError` at `statusCode(201)`; `extract()`, which does nothing but hand back the `ExtractableResponse`, is never invoked and your variable is never assigned. That is the ordering guarantee this style buys: any value you pull out has already survived every check placed above `extract()`. A helper that returns a fitting id should therefore assert the status and the shape of the field it is about to read, rather than leave that to its caller.
code
java · 17 linesimport static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
String fittingId =
given()
.contentType("application/json")
.body("{\"patientRef\":\"P-4417\",\"earSide\":\"LEFT\"}")
.when()
.post("/fittings")
.then()
.statusCode(201)
.header("Location", notNullValue())
.body("status", equalTo("SCHEDULED"))
.body("fittingId", notNullValue())
.extract()
.path("fittingId");go deeper
Be ready to say where extract() goes in the chain and why it comes last. Knowing that a failed statusCode(201) stops the statement before the assignment is enough at this level.
Explain the mechanism, not just the ordering: the response already exists when then() is called, so each expectation validates inside its own method call and throws an AssertionError on the spot.
Show how you use the ordering in a real suite - helpers that assert the status and the field they read before returning it, so a broken service fails at the assertion instead of surfacing later as a null.
Own the convention across a suite. Decide whether helpers assert before extracting or hand callers a raw Response, and be able to defend the choice in terms of where failures are diagnosed and who owns the expectations.
## Where `extract()` sits in the chain REST Assured's fluent call has three phases. `given()` builds the request, `when()` sends it, and `then()` inspects the response that came back. `extract()` is the last step of that third phase: it is declared on `ValidatableResponseOptions` and it returns an `ExtractableResponse`, the object from which you read ordinary Java values — a `String` fitting id, an `int` status code, a `long` round-trip time. Because it is the door out of the validation phase, everything you wrote before it has already run by the time you walk through it. That ordering is not documentation etiquette or a style preference. It falls out of **when** REST Assured executes the assertions, and knowing the mechanism is what lets you rely on it. ## The expectations run as you call them Calling `then()` on a response builds a validatable wrapper and hands it the response object REST Assured is already holding. Every expectation method on that wrapper forwards to an internal response specification that validates **eagerly** — that is, inside the method call — whenever a response is attached, instead of recording the expectation for a later sweep. Concretely, in a chain against `POST /fittings`: - `statusCode(201)` compares the code and throws inside that one call. - `body("status", equalTo("SCHEDULED"))` parses the body and compares in the same call. - `header("Location", notNullValue())` is checked at the point you write it, not at the end. - A failure is a plain `java.lang.AssertionError`, so JUnit or TestNG records a failed test rather than an error. - `extract()` validates nothing of its own — it only hands back the extractable view of the response. The deferred alternative does exist, but it is the older shape: `expect().statusCode(201).when().get(...)`, where expectations are recorded on a specification that has no response yet and are all checked once the request finally goes out. Inside a `then()` chain there is nothing left to defer to, so nothing is deferred. ## What failure does to the statement Because the `AssertionError` is thrown from the middle of the chain, the assignment on the left of the statement never completes. `String fittingId = ...` leaves `fittingId` unassigned; the test stops there. | Where the check sits | What happens when it fails | Does `extract()` run? | |---|---|---| | `then().statusCode(201).extract().path(...)` | `AssertionError` at `statusCode(201)` | No | | `then().body(...).statusCode(...).extract().path(...)` | `AssertionError` at the first failing call | No | | `then().extract().response()`, asserted by hand afterwards | your own assertion fires later | Yes — the value was already read | The third row is the one that catches people out. `extract()` placed before any expectation is perfectly legal and gives you a `Response` object; nothing has been checked, so an error payload is extracted just as happily as a success payload. ## Why the guarantee is worth designing around 1. A value that reaches your variable has survived every check written above it, so the next request in the flow is never built from an error body. 2. The failure is reported at the assertion that actually broke, with REST Assured's own mismatch text, rather than as a `NullPointerException` three lines later. 3. A shared helper that asserts before it extracts cannot hand a caller a plausible-looking `null`. 4. It costs nothing at runtime — the checks were going to run anyway; you are only choosing where they sit. ## Placing expectations deliberately The rule that follows is short: **put every expectation the extracted value depends on above `extract()`.** For a hearing-aid fitting API that means asserting the created status and the shape of the field before reading it: - Assert `statusCode(201)` before extracting `fittingId` from the creation response. - Assert `body("fittingId", notNullValue())` if a downstream call will use that id in a path. - Assert `contentType(ContentType.JSON)` when you are about to read a JSON field, because the extractor picks its parser from the response content type. - Assert only what the extracted value depends on — piling every possible check above `extract()` turns a helper into a second test. ## Where the guarantee stops Ordering protects the value from *unvalidated* responses. It does not make the extraction itself safe: - `extract()` asserts nothing, so it can never fail on its own. - A path that matches nothing yields `null` rather than an error, so `body("fittingId", notNullValue())` above it is what turns a missing field into a clear failure. - A `then()` chain with no expectations at all validates nothing — the guarantee is only as strong as the checks you actually wrote. - Expectations written *after* `extract()`, on a `Response` you extracted, run too late to protect the value you already read. Read that way, "extract after validation" is not a coding-style rule but a description of the chain's control flow: the extraction step is unreachable while an earlier expectation is failing, and that is exactly the property you want when one call's output feeds the next.
- If the assertions are eager, why does a failing then() chain in Java usually report only one broken expectation?Because each expectation validates in its own call, the first one to fail throws immediately and the rest of the chain never executes. You see the first break, not a collected report. Reordering the chain changes which failure you are shown, which is why the cheapest, most diagnostic check — usually the status code — is worth writing first.
- Is it ever legitimate to call extract() before asserting anything?Yes, when the caller owns the expectations — for example a helper that returns the whole `Response` so different tests can assert different things, or a case that needs the body to decide what to assert. Just accept that the extracted value is unvalidated at that moment, and assert the status somewhere before anything downstream consumes it.
- Does extract() itself ever throw?`extract()` only returns the `ExtractableResponse`, so no. What follows it can throw: `path(...)` raises `IllegalStateException` when the response carries no content type and no default parser is configured, and a path matching nothing simply returns `null`. Neither is an assertion failure, which is why the checks above `extract()` are what turn a bad response into a clear test failure.
It behaves like a turnstile rather than a receipt desk: the chain stops you at the first failed check, so nothing downstream ever receives a value that did not pass.
saying these in an interview costs you the question
- Thinks then() collects expectations and validates them all when extract() is called
- Believes extract() re-runs the assertions before handing back a value
- Puts statusCode() after extract() and expects it to still guard the value
- Assumes a failed assertion returns null rather than throwing
- Thinks extract() itself fails the test when the field is missing from the body