Explain RANDOM_PORT vs DEFINED_PORT with TestRestTemplate, and how you get the port if you need it.
answer
- RANDOM_PORT = free port, CI-safe, parallel
- DEFINED_PORT = server.port (8080), conflict risk
- @LocalServerPort injects the real port
- local.server.port property key
- @Value ${server.port} won't give random port
basics
~20 sRANDOM_PORT starts the embedded server on a free random port to avoid clashes; DEFINED_PORT uses the configured server.port (default 8080). The auto-configured TestRestTemplate handles the port for you via relative URLs, or you inject the actual port with @LocalServerPort.
solid answer
~40 sBoth modes start a real embedded servlet container. RANDOM_PORT binds to an OS-assigned free port, which is the safe default for CI where a fixed port might already be in use or where tests run in parallel. DEFINED_PORT uses server.port (default 8080) — useful when something external must reach a known address, but it risks port conflicts. In both cases the auto-configured TestRestTemplate already targets the live port, so you call relative paths like "/api/users". If you need the raw port — to build a URL manually, configure a WebSocket client, or log it — inject it with @LocalServerPort into an int field, or read "local.server.port" from the Environment. Note @LocalServerPort works for RANDOM_PORT and DEFINED_PORT alike.
code
java · 20 lines@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class PortAwareTest {
@Autowired TestRestTemplate rest;
@LocalServerPort int port; // the actual runtime port
@Test
void relativeUrlNeedsNoPort() {
assertThat(rest.getForEntity("/health", String.class)
.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
void absoluteUrlWhenYouNeedThePort() {
String url = "http://localhost:" + port + "/health";
assertThat(rest.getForEntity(url, String.class)
.getStatusCode()).isEqualTo(HttpStatus.OK);
}
}go deeper
Know RANDOM_PORT avoids port clashes and the template uses relative URLs.
Explain both modes, @LocalServerPort/local.server.port, and the @Value gotcha.
Discuss parallelism, port-conflict failure modes, and when DEFINED_PORT is genuinely needed.
Frame port strategy for a large suite: parallel CI, container reuse, and avoiding flaky bind failures.
### The two 'real server' modes `@SpringBootTest(webEnvironment = ...)` controls whether and how a server starts: - **`RANDOM_PORT`** — starts the embedded container (Tomcat by default) on a **random free port** chosen by the OS at bind time. This is the recommended default for integration tests because it prevents 'address already in use' failures when a dev server or another test is holding 8080, and it lets multiple test classes/JVMs run concurrently without collisions. - **`DEFINED_PORT`** — starts the container on the **configured `server.port`** (8080 if unset, or whatever is in `application.properties`/`application-test.yml`). Use only when a fixed, known address matters (e.g., an external process or a hardcoded client must connect). Downsides: port conflicts and no parallelism on that port. Note it also uses the real `server.port`, so it exercises port-related config the way production would. ### How TestRestTemplate copes with an unknown port The auto-configured `TestRestTemplate` bean is wired with a **`LocalHostUriTemplateHandler`** that resolves relative URIs against `http://localhost:<actual-port>`. So your test code stays port-agnostic: `rest.getForEntity("/api/users/1", User.class)`. You never see the number. ### Getting the port when you do need it Sometimes you must know the number — to build an absolute URL, point a non-Spring client at it, set up a WebSocket/STOMP session, or just log it. Two supported ways: 1. **`@LocalServerPort`** (`org.springframework.boot.web.server.LocalServerPort`) on an `int` field or constructor parameter — Spring injects the live port. Works for both RANDOM_PORT and DEFINED_PORT. 2. **Read from the `Environment`**: the property key is `"local.server.port"` — `environment.getProperty("local.server.port", Integer.class)`. > Gotcha: `@Value("${server.port}")` does **not** give you the runtime random port — with RANDOM_PORT `server.port` is effectively 0/unset. Use `@LocalServerPort` / `local.server.port` instead. ### Building your own TestRestTemplate If you construct one manually (`new TestRestTemplate()`), it is **not** pre-bound to the port, so you must supply absolute URLs (e.g., `"http://localhost:" + port + "/api/..."`) or set a root URI yourself. Prefer the auto-configured bean to avoid this. ### Interaction with parallel tests RANDOM_PORT is what makes safe parallel integration testing possible: each context gets its own port. DEFINED_PORT serializes anything sharing that port and will fail fast on conflicts.
- Why is RANDOM_PORT usually preferred over DEFINED_PORT in CI?It binds a free port chosen at runtime, avoiding 'address already in use' conflicts and allowing parallel test execution; DEFINED_PORT pins 8080 (or configured) and can clash or serialize tests.
- Why doesn't @Value("${server.port}") give you the random port?With RANDOM_PORT the configured server.port is 0/unset until the container binds; the actual port is exposed as the runtime property local.server.port, which @LocalServerPort reads.
saying these in an interview costs you the question
- Claiming @Value("${server.port}") returns the random port.
- Thinking you must always build absolute URLs — the auto-configured bean handles the port.
- Assuming RANDOM_PORT does not start a real server (it does; only MOCK/NONE skip it).