skip to content

What are the four webEnvironment modes of @SpringBootTest, and what does each one do?

level: juniorimportance: must knowfreq 70%

answer

  1. MOCK / RANDOM_PORT / DEFINED_PORT / NONE
  2. MOCK = default, no port, MockMvc
  3. RANDOM_PORT = real server + @LocalServerPort + TestRestTemplate
  4. NONE = full context, no web

basics

~10 s

MOCK (default) loads a mock servlet environment, no real port. RANDOM_PORT and DEFINED_PORT start a real embedded server on a random or fixed port. NONE loads the context with no web environment at all.

solid answer

~40 s

@SpringBootTest(webEnvironment=...) picks how the web layer is set up. MOCK (the default) creates a mock servlet environment via MockServletContext — no real HTTP listener, no port — and is used with MockMvc. RANDOM_PORT starts the real embedded server (Tomcat/Jetty/etc.) on a random free port, avoiding clashes in parallel/CI runs; you inject the port with @LocalServerPort and call it with TestRestTemplate or WebTestClient over real HTTP. DEFINED_PORT starts the real server on the port from configuration (e.g. server.port), so it can conflict with a running instance. NONE loads the ApplicationContext with SpringApplication but sets no web environment, so no servlet context and no server — good for testing pure service/persistence beans while still booting the full context.

code

java · 25 lines
java
// MOCK (default): in-process, MockMvc
@SpringBootTest // webEnvironment defaults to MOCK
@AutoConfigureMockMvc
class MockEnvTest {
    @Autowired MockMvc mockMvc;

    @Test
    void greets() throws Exception {
        mockMvc.perform(get("/hello"))
               .andExpect(status().isOk());
    }
}

// RANDOM_PORT: real embedded server, real HTTP
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class RealServerTest {
    @LocalServerPort int port;              // actual random port
    @Autowired TestRestTemplate restTemplate; // auto-configured here

    @Test
    void greets() {
        var body = restTemplate.getForObject("/hello", String.class);
        assertThat(body).isEqualTo("hi");
    }
}

go deeper

for a junior

Name the four modes and that MOCK is the default with no real server.

for a middle

Explain MockMvc vs TestRestTemplate and @LocalServerPort injection.

for a senior

Discuss RANDOM_PORT vs DEFINED_PORT trade-offs and what each mode actually tests (in-process dispatch vs full HTTP stack).

for a principal

Reason about separate-thread transaction semantics, CI parallelism, and choosing the cheapest mode that still validates the risk you care about.

## What this is about `@SpringBootTest` boots a full Spring `ApplicationContext` for an integration test. Its `webEnvironment` attribute decides **whether and how a web/servlet environment is created**. The attribute is the enum `org.springframework.boot.test.context.SpringBootTest.WebEnvironment` with four values. ### 1. `WebEnvironment.MOCK` (the default) - Loads a **`WebApplicationContext`** and provides a **mock servlet environment** (`MockServletContext`). - **No embedded server is started and no real port is opened.** Nothing is listening on TCP. - You exercise controllers through **`MockMvc`** (auto-configured when you add `@AutoConfigureMockMvc`, or via `@WebMvcTest` for slice tests). `MockMvc` dispatches requests through the `DispatcherServlet` **in-process** — it never crosses the network. - Fast; the whole HTTP round-trip (sockets, serialization over the wire) is simulated. ### 2. `WebEnvironment.RANDOM_PORT` - Starts the **real embedded servlet container** (Tomcat by default) listening on a **randomly chosen free port**. - The random port avoids collisions when tests run in parallel or on a CI box where a fixed port might already be taken. - Inject the actual port with **`@LocalServerPort`** (a field or constructor param of type `int`). Equivalent property: `${local.server.port}`. - Call the server with **`TestRestTemplate`** (auto-configured in this mode) or **`WebTestClient`** — these make **real HTTP requests** over the loopback interface. ### 3. `WebEnvironment.DEFINED_PORT` - Starts the real embedded server on the **port defined by configuration** — typically `server.port` (default 8080 if unset). - Because the port is fixed, it can **clash** with another running instance or a parallel test — a common source of `Port already in use` failures. Prefer `RANDOM_PORT` unless you specifically need a known port (e.g. an external system expects it). ### 4. `WebEnvironment.NONE` - Loads the `ApplicationContext` via `SpringApplication` but sets **no web environment**: no servlet context, no embedded server, no port. - Use it when you want the **full context** (services, repositories, config) but the web layer is irrelevant — e.g. testing a `@Service` or a JPA repository through the real bean graph. ## Choosing MockMvc vs TestRestTemplate - **MOCK + MockMvc**: in-process, fast, no real networking. You test the Spring MVC dispatch (controllers, filters registered with MockMvc, argument resolvers, `@ControllerAdvice`). - **RANDOM_PORT/DEFINED_PORT + TestRestTemplate/WebTestClient**: a real server and real HTTP. This exercises the **full stack** — the actual servlet container, real connectors, HTTP message conversion end to end, and things MockMvc can't see (e.g. genuine servlet-container behavior, real ports, external clients). ## Key gotchas - **Default is MOCK** — omitting `webEnvironment` gives you no real server; `TestRestTemplate` is **not** auto-configured there. - `@LocalServerPort` is **null/absent under MOCK and NONE** because there is no port. - Under a **real port** mode the server runs on a **separate thread from the test**, so a `@Transactional` test method does **not** roll back the server-side changes (see follow-ups) — a frequent surprise. - `MockMvc` and `TestRestTemplate` test **different things**: MockMvc validates the MVC layer without the network; TestRestTemplate validates the deployed HTTP endpoint.

  • Is TestRestTemplate auto-configured under WebEnvironment.MOCK?
    No. TestRestTemplate is auto-configured only for the real-server modes (RANDOM_PORT / DEFINED_PORT). Under MOCK there is no listening port to hit, so you use MockMvc instead.
  • Why prefer RANDOM_PORT over DEFINED_PORT?
    RANDOM_PORT picks a free port at runtime, avoiding 'port already in use' clashes with a running app or parallel tests. DEFINED_PORT uses the configured (fixed) port, which can collide.

saying these in an interview costs you the question

  • Thinking MOCK starts a real server on a random port
  • Believing TestRestTemplate works under MOCK
  • Confusing @LocalServerPort with server.port (it's the actual runtime port, only meaningful under real-server modes)

context