Why does a REST Assured test still hit the old host after you set given().baseUri()?
answer
- a scheme in the path changes everything
- absolute URL discards both defaults
- not merged, replaced
- an explicit port still gets appended
- grep verb arguments for ://
basics
~20 sA fully qualified URL in the verb wins. When the path handed to get or request starts with a scheme, REST Assured drops baseUri and basePath and uses that URL's own authority and path. Only an explicit port still applies.
solid answer
~50 sThat test is passing an absolute URL to the verb. If the string given to `get(...)`, `post(...)` or `request(...)` starts with `http://` or `https://`, REST Assured treats it as the complete target: the base URI and the base path are discarded, not merged. So `given().baseUri("https://rota.allotment-water.test").basePath("/api/v2").get("http://legacy-rota.internal/plots/17/slots")` reaches the legacy host, and nothing warns you. The rule is deliberate rather than an oversight: it lets a single test reach an identity host or an object store without disturbing the defaults every other test relies on. The one thing that still applies is an explicitly set port, which is appended when the URL carries none — the wrong host on the right port. Diagnose it by printing the assembled target with `given().log().uri()` or a request-logging filter, then grep the suite for `://` inside verb arguments. The `get(URI)` and `get(URL)` overloads are absolute by construction and bypass the defaults the same way.
code
java · 21 linesimport static io.restassured.RestAssured.given;
// The verb carries a full URL: baseUri AND basePath are discarded
// -> GET http://legacy-rota.internal/plots/17/slots
given()
.baseUri("https://rota.allotment-water.test")
.basePath("/api/v2")
.when()
.get("http://legacy-rota.internal/plots/17/slots")
.then()
.statusCode(200);
// Relative path, so the defaults are used
// -> GET https://rota.allotment-water.test/api/v2/plots/17/slots
given()
.baseUri("https://rota.allotment-water.test")
.basePath("/api/v2")
.when()
.get("/plots/17/slots")
.then()
.statusCode(200);go deeper
Remember the shape of the trap: a verb argument starting with http:// or https:// is the whole address, so the base URI you set above it does nothing for that call.
Explain the precedence precisely — replaced rather than merged — and note the exception that an explicitly set port is still appended when the absolute URL carries none.
Demonstrate the diagnosis: print the assembled URI, confirm the override, then talk about the systemic fix rather than the single edit, including how a wrong-host run should have failed loudly.
Argue for the guardrail rather than the fix: one configurable target, mechanically enforced relative paths, a documented exception process, and a smoke assertion that makes a mis-targeted run impossible to mistake for a green one.
A REST Assured chain that ignores the base URI you just set is almost never a bug in the library. It is the documented precedence rule doing what it was written to do: a fully qualified URL passed to the verb overrides the request's base URI, base path and — when it carries one — its port. ## The rule When the string handed to `get(...)`, `post(...)`, `delete(...)` or `request(...)` starts with a scheme (`http://`, `https://`), REST Assured treats it as the complete target. The base URI and the base path are discarded rather than merged, and the URL's own authority and path are used verbatim. The library's own integration suite names the test for this behaviour `specifyingFullyQualifiedPathOverridesValues`. So on an allotment water-rota API: - `given().baseUri("https://rota.allotment-water.test").basePath("/api/v2").get("/plots/17/slots")` reaches `https://rota.allotment-water.test/api/v2/plots/17/slots`. - The identical chain ending in `.get("http://legacy-rota.internal/plots/17/slots")` reaches `http://legacy-rota.internal/plots/17/slots` — both defaults are gone. Nothing warns you. The chain still compiles, still sends, and often still returns 200 from whatever the old host is serving, which is precisely why this survives a repointing exercise. ## What survives the override and what does not | Setting | Survives a fully qualified verb URL? | |---|---| | `baseUri` / `RestAssured.baseURI` | No — the URL's scheme and authority replace it | | `basePath` / `RestAssured.basePath` | No — the URL's own path is used as written | | `port` / `given().port(...)` | Yes, but only if the URL carries no port of its own | | Headers, cookies, params, auth, filters | Yes — the override is about the target, not the rest of the request | That third row is the subtle one. An explicit port is still appended to a scheme-carrying URL that has no port, so `given().port(9090).get("http://legacy-rota.internal/plots")` reaches port 9090 on the legacy host — the wrong host on the right port. ## Why the library behaves this way The override is a convenience, not an oversight. It lets a single test reach something outside the suite's usual target — a token endpoint on an identity host, a fixture uploaded to object storage, a health check on a neighbouring service — without disturbing the defaults every other test relies on. The `get(URI)` and `get(URL)` overloads exist for the same reason and are fully qualified by construction, so they always bypass the defaults. ## Finding it in a real suite 1. Print the URI the library actually assembled. `given().log().uri()` on the request side, or a request-logging filter registered globally, shows the full target rather than the one you intended. 2. Grep the suite for `://` inside a verb argument. Every hit is a call that ignores the base URI, and most of them are accidental copies from a browser address bar or a curl command. 3. Grep for the `URI` and `URL` overloads of the verbs, which cannot be relative and are easy to miss in a review. 4. Compare a passing local run against the failing deployed run: if one test's target is identical in both, that test is not using the defaults at all. ## Guardrails worth having - Make the verb argument **relative by convention** and enforce it — a one-line assertion in a shared helper, or a lint rule that rejects `://` in a verb argument, catches the whole class. - Keep the environment's host in **one** place so that repointing is a single edit, and treat any test that opts out as something that needs a comment explaining why. - When a call genuinely must leave the suite's target, prefer an explicit `given().baseUri("https://identity.allotment-water.test")` on that chain over an inline absolute URL. The intent is then visible, and the base path and port rules still behave predictably. - Assert on something host-specific in a smoke test — a build identifier or an environment banner in the response — so a run against the wrong host fails on an assertion rather than passing quietly. One more thing to check before blaming the rule: an absolute URL is only one of the ways a chain can leave the suite's target. A test that sets its own `given().baseUri(...)` further up the chain, or one that reuses a helper which does, reaches the same outcome by a legitimate route. The diagnostic is identical in both cases — print the assembled URI and read it — but the fix is different, because an explicit `baseUri(...)` is usually deliberate while an absolute verb path almost never is. The rule itself is short and worth stating exactly as it is: **a scheme in the verb's path means the verb's path is the whole address.**
- Does an explicitly set port still apply when the verb is handed an absolute URL?Yes, when that URL carries no port. `given().port(9090).get("http://legacy-rota.internal/plots")` reaches port 9090 on the legacy host. If the URL already specifies a port, the URL's port wins and the `port(...)` call is ignored. Base URI and base path get no such reprieve — they are discarded outright.
- How would you stop this class of mistake recurring across a large suite?Make relative verb paths a convention and enforce it mechanically: a lint rule or shared helper rejecting `://` in a verb argument catches nearly all of them. Keep the host in one configurable place, require a comment on any deliberate exception, and have a smoke test assert on something host-specific so a wrong-host run fails rather than passes.
saying these in an interview costs you the question
- Thinks the base URI and an absolute verb URL are merged somehow
- Blames a caching or connection-reuse bug in the library
- Believes the base path is still appended to an absolute URL
- Assumes an explicit port() is ignored once a full URL is used
- Says the chain would fail to compile or would throw at runtime