skip to content

REST Assured

4 roadmaps133 questionsupdated

The JVM library for API testing: a given/when/then DSL, JSON and XML body assertions, and built-in auth. The usual answer for exercising REST endpoints from a Java or Kotlin test suite.

on this pageshow

guide

overview

~1 min

REST Assured drives HTTP APIs from a JVM test suite: one fluent chain builds a request, sends it, and states what the response must look like. Interviewers ask about it for two reasons. It is the usual tool for API tests in Java and Kotlin codebases, so junior rounds check that you can write a readable test with it. And its behaviour rests on quiet defaults — which value wins when settings merge, when credentials actually go out, which library turns an object into a body — so senior rounds check whether you can spot a test that passes while proving nothing. The hub follows the chain. [Request DSL & Assertions](/topics/dev-rest-assured-basics) is a whole test end to end and the place to begin. [Outgoing Call Setup](/topics/dev-rest-assured-request) covers where a call is aimed and what it carries. [Response Consumption](/topics/dev-rest-assured-response) is the other half: what `then()` can check and how values are read back out. [Payload Binding](/topics/dev-rest-assured-mapping) is object mapping and schema validation, and [Credential Wiring](/topics/dev-rest-assured-auth) is how a request comes to carry a credential. [Specification Reuse](/topics/dev-rest-assured-spec) is how a suite stops repeating itself, and how shared defaults leak between tests. [Cross-Cutting Hooks](/topics/dev-rest-assured-capture) covers filters and logging; [Harness Placement](/topics/dev-rest-assured-harness) covers client configuration, dependencies, runners and suite-level practice. Junior questions stay on the DSL: which call goes where, how to assert a nested field, how to reuse a value in the next request. Middle and senior rounds turn into diagnosis — a negative test that passes against a broken endpoint, a header sent twice, a base URI silently replaced. Principal questions ask how a large, flaky API suite is made trustworthy. Learn the chain and the response side first; most wrong answers misjudge what a `then()` block really checked. Specifications and filters come next, then authentication, mapping and client configuration.

primer

### One chain, three phases A test is a single fluent expression. `given()` collects the request — target, parameters, headers, body, credentials; `when()` picks the verb and path and sends it; `then()` states expectations about what came back; `extract()` hands values back to ordinary code so the next call can use them. Each step returns a different interface, so the compiler largely enforces the order and decides what you may call next. ### Expectations throw; nothing returns a verdict Every check inside `then()` is evaluated as soon as it is declared and raises an `AssertionError` on a mismatch. Interviewers probe three consequences: a chain that declares no expectation cannot fail at all; anything chained after a failed check, an `extract()` included, never runs; and a check cannot serve as a boolean, which matters the moment a test has to wait for asynchronous work. The library ships no retry or wait of its own. ### Paths and matchers do the asserting Body checks pair a path with a Hamcrest matcher. The path is GPath, Groovy's navigation syntax, so it can index arrays, collect one field across a list, or call a method such as `size()`. JSON and XML each have their own path engine, and XML adds namespaces and attributes to worry about. Whole-document checks — a JSON Schema or an XSD — are matchers too, applied to the body with no path. ### Defaults stack in three scopes Settings can be global (static fields on the `RestAssured` class), shared (a built specification) or local (one call). Global values are defaults that anything narrower overrides. Between a shared specification and settings written into the same chain, though, the merge follows call order: whatever is applied later wins on single-valued settings. Statics are process-wide, so a class that changes them without restoring them can break a class that runs after it. ### Much of the behaviour is borrowed REST Assured wraps other libraries and inherits their rules. The transport is Apache HttpClient, which owns timeouts and the challenge-and-retry flow of non-preemptive authentication. Objects become bodies through whichever mapper is on the classpath, picked by content type. JSON Schema support is a separate module around its own validator. Many senior questions are really "which layer owns this?", and the fix lives in that layer's settings, reached through REST Assured's configuration objects. ### Filters wrap every call Between building the request and validating the response sits a filter chain. Logging, cookie and session handling are filters; so are your own hooks for tokens, correlation ids or rewriting a response before it is checked. A filter must pass control onward, or nothing is sent. Filters that remember cookies or sessions hold that memory in the filter instance, so whether one instance is shared across calls changes what the server sees.

Request specification
The object holding everything a request will carry: target, parameters, headers, body, credentials and filters. given() starts one; a built one can be merged into many calls.
Response specification
A reusable bundle of expectations such as status, headers, content type and body checks, built once and applied to responses from many tests.
RequestSpecBuilder
The builder that assembles a reusable request specification through set and add methods, finished with build(). ResponseSpecBuilder plays the same role for expectations.
GPath
Groovy's path expression syntax, used to navigate a parsed JSON or XML body with dotted fields, array indexes and operations across whole lists.
Hamcrest matcher
A composable predicate object, such as equalTo or hasItems, that describes an expected value and produces a readable description when the actual value does not match.
extract()
The step that leaves validation and returns values to ordinary code: a single path value, a deserialized object, or the whole response.
TypeRef
A generic holder whose anonymous subclass carries a complete generic type, such as a list of a given class, into deserialization despite type erasure.
Filter
A hook that receives each request before sending and returns the response. Filters form a chain; logging, cookie handling and session handling are all built as filters.
Preemptive authentication
Credentials written into the first request's Authorization header without waiting for the server to ask. Needed for servers that never issue a challenge.
Challenged authentication
Credentials the HTTP client sends only after a 401 response asks for them, costing an extra round trip; how plain basic() and digest behave.
RestAssuredConfig
The immutable bundle of client settings, covering the HTTP client, encoding, redirects, logging, object mapping, headers and more. Each setter returns a modified copy.
SessionFilter
A bundled filter that captures a session id from one response and adds it to later requests that pass through the same filter instance.
SpecificationQuerier
A utility that exposes a built request specification as a read-only view, so a unit test can check what a merge produced without sending anything.

Follow one call from the chain to the test runner. When the verb fires, REST Assured assembles the effective request: the static defaults underneath, then each specification and per-call setting in the order the chain applied them. An object body is serialized by a mapper chosen from the Content-Type; strings, byte arrays, files and streams go out as they are. The authentication scheme decides whether an Authorization header is written up front or left for the HTTP client to add after a challenge. The request then runs through the filter chain in order, and the final link is REST Assured's own sender, which hands it to Apache HttpClient. The response travels back through the same filters, which may log it or replace it, and only then reaches `then()`. Each expectation is checked as it is declared. The first failure throws, any failure-only log is printed, and the `AssertionError` unwinds to JUnit or TestNG like any other assertion. If every check passes, `extract()` returns values that seed the next request. The hub's sections attach to stages of that path: - **Outgoing call setup** and **payload binding** shape what leaves the test. - **Credential wiring** is one kind of request state, sometimes fetched by extra calls of its own, as with form login or CSRF tokens. - **Cross-cutting hooks** sit in the middle, on the way out and on the way back. - **Response consumption** is everything after the response arrives. - **Specification reuse** and **harness placement** decide which defaults every stage starts from. Three of those stages meet in this fragment: ```java static final RequestSpecification API = new RequestSpecBuilder() .setBaseUri("https://orders.example.test") .setContentType(ContentType.JSON) .addFilter(new SessionFilter()) // session state lives on this one instance .build(); String id = given().spec(API) // applied first, so later calls can override it .body(new NewOrder("sku-1", 2)) // mapper picked from the JSON content type .log().ifValidationFails() .when().post("/orders") .then().log().ifValidationFails() .statusCode(201) // throws here; extract() below never runs .extract().path("id"); ``` Had the chain set its own base URI before `spec(API)`, the specification would have replaced it without a word. And because `API` is a static shared by every test, so is its session filter, which is convenient in a serial suite and a source of crossed sessions in a parallel one.

  1. Request DSL & Assertions →

    A whole test end to end: the given/when/then chain, body assertions, extracting values and the basic auth options.

  2. Response Consumption →

    What then() really checks and how values come back out; most suite defects that let broken endpoints pass live here.

  3. Outgoing Call Setup →

    Where a call is aimed and what it carries: base URI and port, path and query parameters, headers, bodies and multipart.

  4. Specification Reuse →

    Reusable specifications and global statics, and the merge order that decides which setting actually survives.

  5. Credential Wiring →

    Attaching credentials directly or through a login round trip, a small surface with exact names that are easy to misremember.

  6. Cross-Cutting Hooks →

    Filters and logging: the extension point for cross-cutting behaviour and the only evidence a CI failure leaves behind.

  • Writing a rejected-request test with no expectation in then(): nothing is validated, so a 200 from a broken endpoint passes — see this question.

  • Reaching for a bearer() method that does not exist; a bearer token is attached with auth().oauth2(token).

  • Using plain basic() against an API that never answers 401: the credentials are never sent, where preemptive().basic() would have worked.

  • Calling spec(...) after per-request settings and expecting the local ones to win; the merge follows call order, so the specification overwrites the single-valued ones.

  • Assigning RestAssured statics in one test class without resetting them, then chasing a failure that appears only when the whole suite runs.

  • Using a then() check as a polling loop's condition: catching every AssertionError hides real failures and ends in a timeout that names nothing.

  • Starting from RestAssuredConfig.config() when you meant to extend the current global config, silently dropping every setting made before.

  • Asserting every field of every response, so any harmless API change turns dozens of tests red; assert the behaviour under test and check shape once with a schema.

REST Assured is a client-side library: it sends real HTTP to whatever you point it at, whether a service the test started, a container, or a deployed environment. It is normally paired with JUnit 5 or TestNG as the runner, Hamcrest for matchers, Jackson or Gson for object mapping, Awaitility for waiting on asynchronous state, and WireMock or Testcontainers for the service's own dependencies. A separate module lets the same DSL drive Spring MVC's mock infrastructure without a network, so one style can cover both in-process and black-box tests. Its alternatives split by where the test lives and who writes it. Spring's MockMvc and WebTestClient test a Spring application from the inside: faster, but tied to that framework. A plain HTTP client with AssertJ gives full control at the cost of more code per test. Karate expresses API tests in its own Gherkin-style language, which appeals when people who do not write Java own the suite. Postman collections, run from the command line with Newman, suit exploratory and smoke checks kept outside the build. A defensible answer places REST Assured as the choice when API tests belong in the JVM build, next to the code, written and maintained by engineers.

explore

report an issue with this guide →

questions

133 · 8 sections

What is the REST Assured library used for on the JVM, and how does its given/when/then request DSL work?

level: juniorimportance: must knowfreq 65%
basics
~20 s

REST Assured is a Java library for testing HTTP/REST APIs. You write one fluent chain: given() sets up the request (headers, body, params, auth), when() fires the HTTP call (get/post/put/delete), then() asserts on the response (status, headers, body values).

open as a page

How do you assert on a JSON response body in REST Assured — including nested fields, arrays and "the whole shape" of the payload?

level: middleimportance: must knowfreq 58%
basics
~20 s

Use then().body(path, matcher). The path is a GPath expression — user.address.city, items[0].id, items.size(), items.name — and the matcher is Hamcrest (equalTo, hasItems, containsInAnyOrder). Use rootPath to avoid repeating a prefix, and matchesJsonSchemaInClasspath for whole-payload shape.

open as a page

How does REST Assured let you authenticate requests, and what is the difference between its basic() and preemptive().basic() authentication options?

level: middleimportance: should knowfreq 40%
basics
~20 s

given().auth() offers basic, preemptive basic, digest, form, OAuth 1/2 and certificate auth. preemptive().basic() sends the Authorization header on the first request; plain basic() waits for a 401 challenge before resending with credentials — so it costs an extra round trip and fails against servers that never challenge.

open as a page

With REST Assured, how do you pull values out of a response — for example to reuse an id created by one API call in the next request — and how do you turn a response body into a typed object?

level: middleimportance: should knowfreq 48%
basics
~20 s

End the chain with .extract(). Use extract().path("id") for a single GPath value, extract().response() for the whole Response (status, headers, body, time), or extract().as(User.class) to deserialize into a POJO. Then pass the value into the next given().

open as a page

A REST Assured suite repeats the same base URI, headers and auth in every test, and when a test fails in CI the output shows only a matcher mismatch. What features of the library would you use to fix both problems?

level: seniorimportance: should knowfreq 34%
basics
~20 s

Build shared RequestSpecification and ResponseSpecification objects with RequestSpecBuilder/ResponseSpecBuilder and apply them with given().spec(...) / then().spec(...). For diagnostics, add log().ifValidationFails() on both request and response, or enableLoggingOfRequestAndResponseIfValidationFails(), and use Filters for cross-cutting concerns like tokens and correlation ids.

open as a page

In REST Assured, how do given().baseUri() and given().basePath() decide where a request lands?

level: juniorimportance: must knowfreq 72%
basics
~20 s

REST Assured builds the target URL by joining baseUri, then basePath, then the path given to the verb. Exactly one slash is inserted between the parts. Both methods override the matching static field for that single request only.

open as a page

In REST Assured, what control name and Content-Type does given().multiPart(new File(...)) produce?

level: juniorimportance: must knowfreq 62%
basics
~20 s

Passing a File alone attaches one part under MultiPartConfig's default control name, file, using the file's own name as the filename. Its part type defaults to application/octet-stream. REST Assured also sets the request Content-Type to multipart/form-data by itself.

open as a page

In REST Assured, when does a request actually go to port 8080?

level: middleimportance: must knowfreq 56%
basics
~20 s

REST Assured appends its default port of 8080 only when the target carries no port and its authority is exactly localhost. An explicitly set port always wins over it, and an https target with no port set stays on 443.

open as a page

In REST Assured, how do given().body()'s String, byte[], File and InputStream overloads differ?

level: middleimportance: must knowfreq 55%
basics
~20 s

All four store the argument verbatim as the request body; no object mapper runs. They differ in the Content-Type REST Assured picks when none is set. A byte array or stream gives application/octet-stream, a String or File text/plain.

open as a page

In REST Assured, what does calling queryParam() twice with the same name send, and how do you change it?

level: middleimportance: must knowfreq 57%
basics
~10 s

Both values are sent. REST Assured merges same-name parameters by default, producing zone=north&zone=south. To make the last call win, set ParamConfig's queryParamsUpdateStrategy to UpdateStrategy.REPLACE, or use replaceAllParameters().

open as a page

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

level: juniorimportance: must knowfreq 70%
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.

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

In REST Assured, how do you assert on a response header inside the then() block?

level: juniorimportance: must knowfreq 70%
basics
~20 s

REST Assured's then() block asserts headers with header(name, expectedValue) for an exact string, header(name, Matcher) for anything looser, and headers(Map) for several at once. Content-Type has its own method, contentType(ContentType.JSON). A failed check throws AssertionError and prints every header received.

open as a page

In REST Assured, why can a test of a rejected request pass even when the server returns 200?

level: juniorimportance: must knowfreq 66%
basics
~20 s

REST Assured validates only when at least one expectation exists. A call with no then() block, or a then() that sets no status, status line, body, header, cookie, content type or time check, returns the response and never fails.

open as a page

In REST Assured, how does then().body(path, matcher) differ from then().body(matcher) with no path?

level: middleimportance: must knowfreq 66%
basics
~20 s

REST Assured's body(path, matcher) evaluates the path against the parsed response body and hands the extracted value to the matcher. The no-path body(matcher) form applies the matcher to the whole raw body string instead. Both accept any Hamcrest matcher.

open as a page

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

level: middleimportance: must knowfreq 62%
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.

open as a page

In REST Assured, which value does then().header(name, matcher) check when a header repeats?

level: middleimportance: must knowfreq 46%
basics
~20 s

The last one. REST Assured's header assertion reads Headers.getValue(name), which scans the response headers in reverse and returns the final case-insensitive match, so earlier occurrences are never matched. Extract headers().getValues(name) to assert on every value the response carried.

open as a page

In REST Assured, how do you assert a JSON response against a schema file on the classpath?

level: juniorimportance: must knowfreq 58%
basics
~20 s

REST Assured's schema matcher ships in a separate artifact, io.rest-assured:json-schema-validator, declared alongside rest-assured. Statically import matchesJsonSchemaInClasspath from io.restassured.module.jsv.JsonSchemaValidator and pass it to the no-path body() overload. It validates the entire response body as one assertion, not a single field.

open as a page

In REST Assured, how do you deserialize a JSON array response into a List<ScaffoldInspection>?

level: middleimportance: must knowfreq 58%
basics
~20 s

REST Assured reads a JSON array into a typed list with as(new TypeRef<List<ScaffoldInspection>>() {}); the anonymous subclass carries the element type past erasure. as(List.class) cannot, and hands back raw maps that fail on first use. Import it from io.restassured.common.mapper.

open as a page

In REST Assured, an endpoint returns JSON as text/plain and as(ScaffoldInspection.class) fails — how do you fix it?

level: seniorimportance: must knowfreq 46%
basics
~20 s

REST Assured picks a deserializer from the response content type, and text/plain matches neither JSON nor XML, so as() throws IllegalStateException. Register the type with RestAssured.registerParser, set RestAssured.defaultParser, or name a mapper with as(Class, ObjectMapperType) to bypass the decision.

open as a page

In REST Assured, why does matchesXsd fail when the XSD has an xsd:import, and what fixes it?

level: seniorimportance: must knowfreq 42%
basics
~20 s

A schema handed to matchesXsd as a string or stream carries no base location, so a relative xsd:import or xsd:include cannot be found. Chain using(LSResourceResolver) or with(LSResourceResolver) on the returned XmlXsdMatcher and resolve each referenced file yourself.

open as a page

Why does REST Assured's matchesJsonSchemaInClasspath throw IllegalArgumentException: Schema to use cannot be null?

level: juniorimportance: should knowfreq 42%
basics
~20 s

The classpath lookup returned nothing. matchesJsonSchemaInClasspath resolves its argument with the thread context class loader's getResource, and a miss yields null, which the matcher factory rejects immediately. Nothing was validated, so this is a wiring bug, not a schema mismatch.

open as a page

In REST Assured, how do you authenticate every request by default and opt one test out?

level: juniorimportance: must knowfreq 58%
basics
~10 s

Assign a scheme to the static field RestAssured.authentication, for example RestAssured.basic(user, pass). It applies to any request that sets no scheme of its own. A single request opts out with given().auth().none().

open as a page

In REST Assured, how do you carry a logged-in session id from one request to the next?

level: juniorimportance: must knowfreq 61%
basics
~20 s

Read the session id off the login response with sessionId(), then hand it to the next call as given().sessionId(value). REST Assured writes it as the JSESSIONID cookie. One SessionFilter instance added to both requests does that capture-and-replay for you automatically.

open as a page

REST Assured has no auth().bearer() method - so how do you attach a bearer token to a request?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Use given().auth().oauth2(token) - REST Assured has no bearer method at all. With the default HEADER signature that call installs a PreemptiveOAuth2HeaderScheme, which writes a plain Authorization: Bearer header on the request, with no signing and no challenge round trip.

open as a page

In REST Assured, how does given().csrf(path) decide to send the token as a header or a form parameter?

level: middleimportance: must knowfreq 36%
basics
~20 s

REST Assured GETs that path and scans the HTML. By default it prefers a meta tag named _csrf_header and sends the value as the X-CSRF-TOKEN header. Otherwise it uses the hidden input named _csrf as a form parameter.

open as a page

In REST Assured, which objects do auth().basic(...) and auth().preemptive().basic(...) install on a request?

level: middleimportance: must knowfreq 64%
basics
~20 s

auth().basic(user, pass) installs a BasicAuthScheme that hands the credentials to the underlying Apache HttpClient, so the Authorization header goes out only after a 401. auth().preemptive().basic(...) installs no scheme at all and writes the header directly.

open as a page

In REST Assured, how do you print the request and response only when a validation fails?

level: juniorimportance: must knowfreq 68%
basics
~20 s

REST Assured buffers the log instead of printing it when you chain given().log().ifValidationFails() on the request or then().log().ifValidationFails() on the response. The buffered text reaches the console only if a then() expectation fails. RestAssured.enableLoggingOfRequestAndResponseIfValidationFails() switches both sides on globally.

open as a page

In REST Assured, which log() calls narrow the output to just the headers, cookies or body?

level: juniorimportance: must knowfreq 64%
basics
~20 s

REST Assured narrows log output instead of printing everything: body(), headers() and cookies() exist on both sides, method(), uri() and params() only on the request, status() only on the response. Each call registers its own logging filter.

open as a page

In REST Assured, how does a custom filter change the response before then() validates it?

level: middleimportance: must knowfreq 55%
basics
~20 s

Return a new Response built with ResponseBuilder: clone the response that ctx.next handed back, override the body, headers or status code, then build. REST Assured validates whatever your filter returns, so the rebuilt copy is what then() and extract() see.

open as a page

Two custom REST Assured filters must run in a fixed order — how do you guarantee it?

level: seniorimportance: must knowfreq 46%
basics
~20 s

Make each filter implement OrderedFilter and return a getOrder value; REST Assured sorts the whole filter list by that number before sending, lowest first. A plain Filter counts as DEFAULT_PRECEDENCE, one thousand, so give the earlier filter a smaller order.

open as a page

What filters ship with REST Assured out of the box, and how do you register one on a request?

level: juniorimportance: should knowfreq 58%
basics
~20 s

REST Assured ships seven ready-made filters: RequestLoggingFilter, ResponseLoggingFilter, ErrorLoggingFilter, StatusCodeBasedLoggingFilter, TimingFilter, CookieFilter and SessionFilter. Four of them print, one records elapsed time, and two remember state between calls. Register one with given().filter(...), or on every call through RestAssured.filters(...).

open as a page

In REST Assured, how do you apply a RestAssuredConfig globally, to one specification, and to a single request?

level: juniorimportance: must knowfreq 58%
basics
~20 s

RestAssuredConfig applies at three scopes: assign it to the static RestAssured.config field for every request, pass it to RequestSpecBuilder.setConfig for one reusable specification, or hand it to given().config for a single call. A narrower scope overrides a wider one.

open as a page

In REST Assured, how do you wait for a tram fare-inspection to become SETTLED before asserting?

level: middleimportance: must knowfreq 62%
basics
~20 s

REST Assured has no retry, no polling and no timeout method on its request DSL. You write the waiting yourself, in a bounded loop or a library such as Awaitility, and REST Assured supplies each call and the final assertion.

open as a page

In REST Assured, how do RestAssuredConfig.config() and RestAssured.config() differ when you add a setting?

level: middleimportance: must knowfreq 46%
basics
~20 s

RestAssuredConfig.config() is a factory returning a brand-new, all-default config; RestAssured.config() returns whatever is currently assigned to the static field. Building from the factory silently discards earlier settings, because each setter copies the other seventeen objects forward unchanged.

open as a page

When a REST Assured expectation fails, what is thrown and how does JUnit 5 or TestNG see it?

level: middleimportance: must knowfreq 66%
basics
~20 s

REST Assured throws a plain java.lang.AssertionError whose message opens with a count of failed expectations and lists each mismatch. Nothing catches it, so it unwinds out of the test method and the runner that called that method records a failure.

open as a page

Why should a REST Assured polling loop not use then().statusCode(200) as its loop condition?

level: seniorimportance: must knowfreq 47%
basics
~20 s

Because REST Assured validation is not a boolean: a failed expectation throws AssertionError. Using it as a loop condition means catching AssertionError every iteration, which silently swallows real failures as not-ready-yet and ends with a timeout message naming nothing.

open as a page