skip to content

Deep Field Access

Getting at a value buried inside a response: the expression language over a JSON payload, the node and namespace model over XML, and the extraction step that runs once the assertions pass.

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

explore

questions

13

In REST Assured, what happens to a then().statusCode(201).extract().path(...) chain when the status check fails?

level: middleimportance: must knowfreq 62%

answer

  1. assertions fire as you call them
  2. nothing is queued for the end
  3. the chain throws before extract()
  4. AssertionError at the failing expectation
  5. expectations above extract(), not below

basics

~20 s

Nothing 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 s

REST 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 lines
java
import 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

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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

In REST Assured, why is calling extract().path() six times on one response worse than holding a JsonPath?

level: seniorimportance: must knowfreq 51%

basics

~20 s

Each extract().path(...) call builds a fresh path object over the body, so six calls parse the same document six times. Hold one extract().jsonPath() instead: it caches its parse after the first read and takes a JsonPathConfig.

open as a page

In REST Assured, why does an XML path with a namespace prefix return an empty string?

level: seniorimportance: must knowfreq 45%

basics

~20 s

REST Assured resolves a path prefix through XmlConfig's declared namespaces, not the document's. Until declareNamespace(prefix, uri) binds that exact URI, a prefixed step matches nothing and reads back as an empty string; setting namespaceAware alone does not help.

open as a page

In REST Assured, what can then().extract() give you besides a value from the response body?

level: juniorimportance: should knowfreq 55%

basics

~20 s

Extraction is not only about body fields. The same ExtractableResponse yields statusCode(), statusLine(), contentType(), header(name), headers(), cookie(name), cookies(), detailedCookie(name), detailedCookies(), sessionId(), time(), timeIn(TimeUnit) and response(), plus whole-body forms such as asString(), asPrettyString(), asByteArray() and asInputStream(). One interface, not several.

open as a page

In REST Assured, why does then().body(path, equalTo(0.82)) fail on a JSON decimal?

level: juniorimportance: should knowfreq 45%

basics

~20 s

REST Assured parses a JSON decimal into a Float by default. Hamcrest's equalTo compares with equals, so a Java double literal like 0.82 never matches the parsed Float. Write 0.82f instead, or change the configured number return type.

open as a page

In REST Assured, how do you read an XML element and its attributes with GPath?

level: juniorimportance: should knowfreq 52%

basics

~20 s

REST Assured evaluates a dotted Groovy GPath over the parsed XML tree: each dot is a child-element step, an attribute step carries @, and text() returns character data. Indexes, size() and closures work; a missing attribute reads as null.

open as a page

In REST Assured, why does the JsonPath expression $.glossary.entry[0].term return null?

level: middleimportance: should knowfreq 48%

basics

~20 s

REST Assured's JsonPath speaks Groovy GPath, not the Jayway JsonPath syntax other tools use. GPath has no dollar-sign root, so that fragment is read as an ordinary property name, finds nothing, and the chain collapses to null. Write glossary.entry[0].term instead.

open as a page

In REST Assured, what does XmlPath.CompatibilityMode.HTML change about parsing a body?

level: middleimportance: should knowfreq 38%

basics

~20 s

XmlPath.CompatibilityMode swaps the parser, not the path language. Its XML constant builds an XmlSlurper from XmlConfig's validating, namespaceAware and allowDocTypeDeclaration flags; its HTML constant wraps the same slurper around tagsoup, which repairs ill-formed markup instead of rejecting it.

open as a page

In REST Assured, how do you put a runtime value into a JsonPath GPath expression safely?

level: seniorimportance: should knowfreq 34%

basics

~20 s

A JSON GPath expression is compiled and run as Groovy, so concatenating a runtime value into it is code injection. Bind the value safely with JsonPath.param(name, value) instead. The expression then references it by name inside the closure.

open as a page

In REST Assured, which XmlConfig switches decide whether a DOCTYPE in a response parses?

level: seniorimportance: should knowfreq 31%

basics

~20 s

XmlConfig.allowDocTypeDeclaration(boolean) decides it, and its default is false, so a body carrying a DOCTYPE fails to parse. disableLoadingOfExternalDtd() is a separate switch that only stops the parser fetching the referenced DTD, and validating(boolean) likewise defaults to false.

open as a page

Your REST Assured helpers all end in extract().response() and assert later — what does that convention cost?

level: principalimportance: should knowfreq 38%

basics

~20 s

You give up the ordering guarantee. Nothing has been asserted when the value is produced, so a failed call yields null, the failure surfaces later as an unrelated error, and you lose REST Assured's own mismatch message.

open as a page

In REST Assured, which settings control how JsonPath materialises JSON numbers?

level: seniorimportance: nice to knowfreq 27%

basics

~20 s

REST Assured's numberReturnType controls how JSON numbers are materialised, with four values: FLOAT_AND_DOUBLE (the default), BIG_DECIMAL, DOUBLE and BIG_INTEGER. Set it on JsonConfig for the DSL or JsonPathConfig standalone. Both also cap numeric literals via numberLengthLimit.

open as a page

Your REST Assured suite asserts through long GPath closures — how much logic belongs in the expression?

level: principalimportance: nice to knowfreq 26%

basics

~20 s

Keep a GPath expression to one filter or projection you can read at a glance. Move multi-stage computation into Java. The path is an unchecked string compiled as Groovy, so mistakes surface at runtime, never at compile time.

open as a page