skip to content

webEnvironment Modes

MOCK gives you a mock servlet environment for MockMvc, RANDOM_PORT starts a real server for TestRestTemplate or WebTestClient, NONE skips the web layer entirely. Choosing correctly is the difference between testing your controller and testing HTTP.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

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

open as a page

When would you use MOCK + MockMvc versus RANDOM_PORT + TestRestTemplate, and what does each actually exercise?

level: middleimportance: must knowfreq 68%

basics

~10 s

Use MOCK + MockMvc for fast in-process controller tests that never touch the network. Use RANDOM_PORT + TestRestTemplate when you need a real embedded server and real HTTP requests to test the full stack.

open as a page

How do you obtain the actual port under RANDOM_PORT, and how does @LocalServerPort differ from server.port?

level: middleimportance: should knowfreq 52%

basics

~10 s

Inject an int field annotated with @LocalServerPort (or read the ${local.server.port} property). It holds the real random port the embedded server bound to at runtime, which under RANDOM_PORT differs from any configured server.port.

open as a page

When is WebEnvironment.NONE the right choice, and how does it differ from a plain non-web @SpringBootTest?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Use NONE when you want the full application context but no web layer — for testing services, repositories, or scheduled/messaging beans. It loads via SpringApplication with no servlet context, no embedded server, and no port.

open as a page

Under RANDOM_PORT, a @Transactional test issues an HTTP POST that creates a row, yet the row isn't rolled back after the test. Why, and how do you handle it?

level: principalimportance: should knowfreq 38%

basics

~20 s

The embedded server handles the request on a different thread with its own transaction, so the test's transaction can't wrap or roll it back. Clean up explicitly — @Sql, delete in teardown, @DirtiesContext, or reset the datastore.

open as a page