skip to content

In REST Assured, how do you change the JSON schema draft or switch off checked validation?

level: middleimportance: should knowfreq 38%

answer

  1. the draft is the wrapped library's
  2. using(...) for one assertion
  3. JsonSchemaValidator.settings for all
  4. every wither returns a new instance
  5. reset() nulls the static field

basics

~20 s

Both settings belong to the wrapped library and are reached through JsonSchemaValidatorSettings. Apply them to one assertion with matchesJsonSchemaInClasspath(...).using(factoryOrSettings), or to every matcher created afterwards by assigning the public static field JsonSchemaValidator.settings; JsonSchemaValidator.reset() puts it back to null.

solid answer

~40 s

Neither knob is REST Assured's own — the draft lives on the wrapped library's `JsonSchemaFactory`, and checked-versus-unchecked is a choice between its two validation calls. REST Assured exposes both through `JsonSchemaValidatorSettings` at **two scopes**. Per assertion: `matchesJsonSchemaInClasspath("schemas/carbon-survey-schema.json").using(settings().with().jsonSchemaFactory(draftV4).and().with().checkedValidation(false))`, where `using(...)` also accepts a bare `JsonSchemaFactory`. Globally: assign the public static field `JsonSchemaValidator.settings`, which every matcher reads **at construction time**, and call `JsonSchemaValidator.reset()` to null it again. Two traps follow from the design. `JsonSchemaValidatorSettings` is immutable — `checkedValidation(false)`, `jsonSchemaFactory(f)` and `parseUriAndUrlsAsJsonNode(true)` each return a *new* instance, so a call whose result you discard does nothing. And `settings` is plain shared mutable state read at matcher-construction time, so leaving it set leaks into every later test in the same JVM, and two parallel classes configuring it differently will race.

code

java · 19 lines
java
import static io.restassured.module.jsv.JsonSchemaValidatorSettings.settings;

// DRAFTV4 and ValidationConfiguration come from the wrapped library
JsonSchemaFactory draftV4 = JsonSchemaFactory.newBuilder()
        .setValidationConfiguration(ValidationConfiguration.newBuilder()
                .setDefaultVersion(DRAFTV4).freeze())
        .freeze();

// scope 1: this assertion only
get("/surveys/PEAT-2291").then().body(
        matchesJsonSchemaInClasspath("schemas/carbon-survey-schema.json")
                .using(settings().with().jsonSchemaFactory(draftV4)
                        .and().with().checkedValidation(false)));

// scope 2: every matcher created after this line
JsonSchemaValidator.settings = settings().with().jsonSchemaFactory(draftV4);

// back to the defaults
JsonSchemaValidator.reset();

go deeper

for a junior

Know that the draft and the checked flag are not REST Assured's own, and that using(...) applies a configuration to a single assertion. Recognising JsonSchemaValidatorSettings by name is enough at this stage.

for a middle

Explain both scopes precisely: using(...) per assertion versus the static field read at matcher-construction time, plus reset(). Be able to say why a discarded wither result silently does nothing.

for a senior

Show how you keep the static field from leaking across a suite — a suite-level fixture or per-assertion using(...) — and why a parallel run makes the shared static a genuine flake source.

for a principal

Own the standard: whether the codebase pins a draft at all, where that decision is recorded, and how you stop per-test global configuration from becoming ambient state nobody can reason about.

## Two scopes, one settings object REST Assured's schema matcher is a thin wrapper, so both questions in the title are really questions about the library it wraps. The bridge is `io.restassured.module.jsv.JsonSchemaValidatorSettings`, which carries exactly three things: a `JsonSchemaFactory`, a `checkedValidation` flag, and a `parseUriAndUrlsAsJsonNode` flag. Construct one with the static factory `settings()`, or with `new JsonSchemaValidatorSettings()`, and chain through the no-op readability methods `and()` and `with()`, which both return `this`. You can apply that object at two scopes. - **One assertion.** `matchesJsonSchemaInClasspath(...)` returns a `JsonSchemaValidator`, which declares `using(JsonSchemaFactory)` and `using(JsonSchemaValidatorSettings)`. Both build a fresh matcher and return it as a `Matcher<?>`; neither touches anything global. - **Every matcher created from now on.** `JsonSchemaValidator.settings` is a **public static field**. Assign it and each subsequent `matchesJsonSchema*` call picks it up; `JsonSchemaValidator.reset()` sets it back to `null` and the defaults return. ## Choosing the draft REST Assured has no draft enum of its own — searching for one is a dead end. The default version is configured on the wrapped library's factory, and passed in: ```java JsonSchemaFactory draftV4 = JsonSchemaFactory.newBuilder() .setValidationConfiguration(ValidationConfiguration.newBuilder() .setDefaultVersion(DRAFTV4) // constant from the wrapped library .freeze()) .freeze(); get("/surveys/PEAT-2291").then() .body(matchesJsonSchemaInClasspath("schemas/carbon-survey-schema.json").using(draftV4)); ``` The default, when you configure nothing, is `JsonSchemaFactory.byDefault()`. Whatever drafts that factory understands are the drafts your assertions understand — which is why the artifact version of the wrapped library, not the REST Assured version, is what to check when a schema keyword behaves unexpectedly. ## Checked versus unchecked validation The wrapped library offers two validation calls, and `checkedValidation` picks between them: `true` (the default) uses the checked call, `false` uses the unchecked one. REST Assured's matcher reads the flag through `shouldUseCheckedValidation()` at match time and dispatches accordingly. Set it per assertion with `.using(settings().with().checkedValidation(false))`, or globally on the static field. Treat it as a choice about **how a processing problem is raised**, not as a switch that turns a schema mismatch into a pass — a payload that violates the schema still fails either way. ## The settings object is immutable This catches people who expect a JavaBean: - `checkedValidation(boolean)` returns a **new** `JsonSchemaValidatorSettings`. - `jsonSchemaFactory(JsonSchemaFactory)` returns a **new** instance. - `parseUriAndUrlsAsJsonNode(boolean)` returns a **new** instance. - `and()` and `with()` return `this` and exist only so the chain reads as a sentence. - The constructor rejects a `null` factory with `IllegalArgumentException`. So `JsonSchemaValidatorSettings s = settings(); s.checkedValidation(false);` changes nothing at all — the new object is discarded. Always keep the return value: `s = s.checkedValidation(false);`, or build the whole thing in one chain. The same immutability is why `matcher.using(factory)` is safe: internally it calls `instanceSettings.jsonSchemaFactory(factory)`, producing a new settings object for that one matcher and leaving the static field untouched. ## The static field is shared mutable state `public static JsonSchemaValidatorSettings settings;` is as blunt as it looks, and three consequences follow: 1. **It is read when a matcher is created, not when it validates.** A matcher built before you assign the field keeps the defaults, and one built while the field is set keeps that configuration even if you reset afterwards. 2. **It leaks across tests.** Nothing scopes it to a class or a method, so a test that sets it and does not clean up changes every later assertion in the JVM. 3. **It is not thread-safe.** There is no synchronisation, so two test classes running in parallel and configuring it differently will interleave unpredictably. The defensible pattern is one of two extremes. Either the whole suite genuinely wants the same configuration — set it once in a suite-level fixture and never touch it again — or configuration is per-case, in which case use `.using(...)` on the individual assertion and leave the static alone. The unstable middle is a test that sets the static for its own benefit; if you must, pair it with `JsonSchemaValidator.reset()` in teardown. ## The third setting, briefly `parseUriAndUrlsAsJsonNode` (default `false`) decides how a schema named by `URL` or `URI` is handed on. Left `false`, the URL's string form goes to the factory, which dereferences it as a URI; set `true`, REST Assured loads the document into a node first and passes that. This matters for `matchesJsonSchemaInClasspath`, because that method funnels into the `URL` overload — so a classpath schema takes the URI route by default.

  • Two test classes set JsonSchemaValidator.settings differently and run in parallel. What happens?
    They race. `settings` is a plain public static field with no synchronisation, and every matcher copies whatever reference it sees at construction, so a matcher built in one thread can pick up the other thread's factory. Set it once in a suite-level fixture if the whole suite wants one configuration; otherwise use per-assertion `using(...)`, which builds a fresh settings object and leaves the static untouched.
  • What are the defaults if you never configure settings at all?
    `new JsonSchemaValidatorSettings()` gives `JsonSchemaFactory.byDefault()`, `checkedValidation` true and `parseUriAndUrlsAsJsonNode` false. So the draft is whatever the wrapped library's default factory chooses, validation is checked, and a schema named by `URL` or `URI` — which includes every `matchesJsonSchemaInClasspath` call — is passed on as a URI string rather than parsed into a node first.

Assigning JsonSchemaValidator.settings is like changing the default paper size on a shared office printer: every job submitted afterwards inherits it, jobs already queued keep the old setting, and nobody else in the room was told.

saying these in an interview costs you the question

  • Hunts for a draft enum on REST Assured itself
  • Calls settings.checkedValidation(false) without reassigning the result
  • Thinks the static settings field affects matchers created before it was set
  • Leaves JsonSchemaValidator.settings set for the rest of the suite
  • Believes using(...) mutates the global settings object
  • Expects checkedValidation(false) to make a mismatching payload pass