Your REST Assured CI run prints nothing on a failing case despite enableLoggingOfRequestAndResponseIfValidationFails(); how do you diagnose it?
answer
- the assertion has to be REST Assured's own
- eager validation punishes chain order
- one LogConfig, one assignment
- an existing logging filter suppresses the wiring
basics
~20 sREST Assured flushes a failure log only when an expectation inside its own then() chain throws, so the usual cause is an assertion outside that chain. Then check the call's position, and whether a later LogConfig assignment dropped the switch.
solid answer
~50 sWork down four causes in order of likelihood. **One:** the failing assertion is not a REST Assured expectation — a JUnit or AssertJ assertion after `extract()` never fires the validation-failure listener that does the flushing, and this is the answer most of the time. **Two:** on a hand-chained case, `log().ifValidationFails()` sits after the failing assertion in the `then()` chain, which validates eagerly, so the buffer was never registered. **Three:** the config never reached this request — a `RequestSpecBuilder` constructed before the static ran snapshotted the old config, a spec with its own user-configured `LogConfig` won the merge, or a later `RestAssured.config` assignment built a fresh `LogConfig` and dropped the flag. **Four:** the side already carried a logging filter, so REST Assured skipped adding the buffered one. Prove which by pointing the log stream at a stream you own and asserting on it.
code
java · 11 linesimport io.restassured.RestAssured;
import static io.restassured.config.LogConfig.logConfig;
// one LogConfig carries both settings - a second assignment would drop one
RestAssured.config = RestAssured.config().logConfig(logConfig()
.enableLoggingOfRequestAndResponseIfValidationFails()
.blacklistDefaultSensitiveHeaders()
.blacklistHeader("X-Club-Key"));
RestAssured.baseURI = "https://ladder.curlingclub.test";go deeper
Know the first cause and check it before anything else: if the assertion that failed was a JUnit one written after extract(), REST Assured never saw a validation failure and had nothing to print.
Be able to explain the wiring — the switch is a LogConfig setting applied while the request is built, and the flush is a listener inside response validation — because every cause is a break in that specific chain.
Demonstrate the ordered diagnosis and the proof step: redirect the log stream and assert on it to separate a listener that never fired from output the run failed to capture, rather than guessing at the pipeline.
Set the convention that removes the class of failure: assert inside the chain, configure logging once in a single chained LogConfig before any specification is built, and make that the reviewed default across suites.
## What the switch actually promises `RestAssured.enableLoggingOfRequestAndResponseIfValidationFails()` is a shortcut for a `LogConfig` setting. When a request is built and that setting is on, REST Assured wires a buffered request log and a buffered response log for that call; a validation-failure listener flushes them if an expectation fails. Every word in that sentence is a place the chain can break, which is why "it's on and nothing printed" is a real diagnosis rather than a bug report. Work the four causes in order — they are roughly in order of how often they turn out to be the answer. ## Cause one: the assertion is not a REST Assured expectation This is the answer most of the time. The listener fires from inside REST Assured's response validation. It sees expectations declared in the `then()` chain and nothing else. So a case written as: ```java Ladder ladder = given().get("/rinks/rink-3/ladder").then().extract().as(Ladder.class); assertEquals("Aitken", ladder.topRung().skip()); // JUnit, outside REST Assured ``` fails at an assertion REST Assured has no knowledge of. No validation failed, so nothing flushes. The fix is to move the check the log is supposed to explain into the chain — `then().body("rungs[0].skip", equalTo("Aitken")).extract()...` — or to log unconditionally on the cases that assert outside it. The same reasoning covers a connection refusal, a DNS failure or a TLS handshake error: those throw before `then()` is reached, so no validation begins and no flush happens. ## Cause two: the log call is after the failing assertion On cases that chain `log().ifValidationFails()` by hand rather than relying on the global switch, position matters on the response side. A `then()` chain validates eagerly, expectation by expectation, so `then().statusCode(200).log().ifValidationFails()` throws at `statusCode` before the buffer is ever registered. The request-side call is immune — it registers a filter that runs at send time — but the response-side one must come first in the chain. ## Cause three: the config never reached this request Three distinct variants, all of which look identical from the outside: 1. **A builder snapshotted the old config.** A `RequestSpecBuilder` reads the `RestAssured` statics, including `config`, in its constructor. A spec built in a static initialiser that runs before the `@BeforeAll` calling the switch carries the pre-switch config forever. 2. **A spec won the merge.** Config is merged per config object by whether that object is user-configured. A request specification carrying its own `LogConfig` replaces yours for that request. 3. **A second assignment discarded the flag.** `LogConfig` is immutable and `logConfig()` starts from defaults, so `RestAssured.config = RestAssured.config().logConfig(logConfig().blacklistDefaultSensitiveHeaders())` executed after the switch throws the switch away. The reverse order loses the blacklist instead, because the static shortcut also builds its `LogConfig` from defaults. **Every log setting the suite needs must be chained onto one `LogConfig`.** ## Cause four: something already logs on that side When the switch is on, REST Assured adds the buffered request log only if no `RequestLoggingFilter` is registered on the request, and the buffered response log only if no `ResponseLoggingFilter` is. That guard exists to stop double printing, and it is usually invisible — a spec that already carries `log().all()` prints everything anyway. It bites when the registered filter is conditional: a `ResponseLoggingFilter` narrowed to a status matcher suppresses the buffered response log, and then prints nothing itself when the failure is a body mismatch on a 200. ## Proving it rather than guessing A short checklist that separates "the listener never fired" from "the output was written somewhere you are not looking": 1. Reproduce locally with one deliberately failing ladder assertion, written inside the `then()` chain. If that prints, cause one is your answer for the CI case. 2. Point the log stream at a `PrintStream` you own and assert on its contents in that failing case. Empty means the flush never happened; full means the problem is downstream in how the run captures output. 3. Query the built specification and look at what filters it actually carries, rather than at what you believe the base class added. 4. Check where the switch is called relative to every `RequestSpecBuilder` construction and every `RestAssured.config` assignment in the suite. 5. Grep the suite for assertions that follow `extract()` — that count is usually the real size of the problem. ## Making it stick The durable fix is structural rather than per-test: assert inside the chain by default so the failure log has something to hook onto, configure the switch and the header blacklist once in a single `LogConfig` chain, and keep that configuration in one place that runs before any specification is built. A suite that follows those three rules does not produce silent failures in CI.
- Why would the log print but arrive with the Authorization header unmasked?Because `RestAssured.enableLoggingOfRequestAndResponseIfValidationFails()` builds a fresh `LogConfig` from defaults and assigns it, discarding a blacklist configured by an earlier assignment. Chain both onto one `LogConfig`. Separately, a response printed through a `ResponseLoggingFilter` is constructed with an empty blacklist, so never rely on redaction on that path.
- How do you prove the flush is not firing rather than the output being swallowed?Point the log stream at a `PrintStream` you own and assert on its contents in one deliberately failing case. If it stays empty the listener never fired, which puts the cause in the chain or the config. If it fills, the flush works and the problem is downstream in how the run captures output.
saying these in an interview costs you the question
- Assumes a JUnit assertion after extract() triggers the failure log
- Thinks the global switch survives any later RestAssured.config assignment
- Believes chain order inside then() cannot matter
- Expects the failure log to appear for a connection timeout
- Assumes a RequestSpecBuilder built earlier picks the static up later
- Blames the CI runner before checking where the assertion lives