skip to content

Explain RANDOM_PORT vs DEFINED_PORT with TestRestTemplate, and how you get the port if you need it.

level: middleimportance: should knowfreq 55%

answer

  1. RANDOM_PORT = free port, CI-safe, parallel
  2. DEFINED_PORT = server.port (8080), conflict risk
  3. @LocalServerPort injects the real port
  4. local.server.port property key
  5. @Value ${server.port} won't give random port

basics

~20 s

RANDOM_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 s

Both 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
java
@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

for a junior

Know RANDOM_PORT avoids port clashes and the template uses relative URLs.

for a middle

Explain both modes, @LocalServerPort/local.server.port, and the @Value gotcha.

for a senior

Discuss parallelism, port-conflict failure modes, and when DEFINED_PORT is genuinely needed.

for a principal

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).

context