skip to content

How do you run an MVC slice/integration test as an authenticated user without performing a real login? Explain @WithMockUser.

level: juniorimportance: must knowfreq 80%

answer

  1. seeds SecurityContext before test, clears after
  2. default user/ROLE_USER, roles auto-prefix ROLE_
  3. WithSecurityContextTestExecutionListener
  4. no real auth, no filter chain
  5. still need .with(csrf()) for POST

basics

~20 s

Annotate the test method (or class) with @WithMockUser from spring-security-test. Before the test runs it seeds the SecurityContext with a fake authenticated user (default name 'user', role 'ROLE_USER'), so security checks pass without a real login.

solid answer

~40 s

@WithMockUser (in spring-security-test) populates the SecurityContextHolder with a UsernamePasswordAuthenticationToken before the test body executes, and clears it afterward. Attributes let you customize identity: username/value for the name, roles=... which auto-prefix ROLE_, or authorities=... for raw, unprefixed authorities (you can't use both). Because it runs via a JUnit test-execution listener (WithSecurityContextTestExecutionListener, auto-registered by @SpringBootTest / spring-security's test setup), no password check, no filter chain, no HTTP round-trip happens. It works for any test that reads the SecurityContext — including MockMvc method-security tests. Pair it with .with(csrf()) on mutating MockMvc requests since it doesn't add a CSRF token. Put it on the class to apply to every method, or on a method to override.

code

java · 19 lines
java
@WebMvcTest(AccountController.class)
class AccountControllerTest {

  @Autowired MockMvc mvc;

  @Test
  @WithMockUser // name "user", authority ROLE_USER
  void getProfile_authenticated_returns200() throws Exception {
    mvc.perform(get("/api/me"))
       .andExpect(status().isOk());
  }

  @Test
  @WithMockUser(username = "admin", roles = {"ADMIN"}) // ROLE_ADMIN
  void deleteUser_asAdmin_ok() throws Exception {
    mvc.perform(delete("/api/users/42").with(csrf())) // CSRF still required
       .andExpect(status().isNoContent());
  }
}

go deeper

for a junior

Know it makes the test run as a logged-in user and roles get ROLE_ prefixed; remember csrf() for POST.

for a middle

Explain the listener mechanism, roles-vs-authorities, class-vs-method placement, and that no real login happens.

for a senior

Contrast with @WithUserDetails/@WithSecurityContext for custom principals; know setupBefore and when it isn't the right tool (testing the actual login path).

for a principal

Reason about composed meta-annotations for reusable identities and the boundary between context-seeding tests and full filter-chain/authentication-flow tests.

## What it is `@WithMockUser` is an annotation from the **spring-security-test** module (`org.springframework.security.test.context.support.WithMockUser`). It sets up a fully-populated `SecurityContext` *before* your test method body runs, so any code that calls `SecurityContextHolder.getContext().getAuthentication()` — or any authorization rule like `@PreAuthorize`, `.authenticated()`, method security — sees a logged-in principal. It removes the context after the test. ## The mechanism Spring Security registers a `WithSecurityContextTestExecutionListener` (a JUnit `TestExecutionListener`). When it sees a `@WithMockUser` (or any `@WithSecurityContext`-meta-annotated annotation) on the test method or class, it builds a `SecurityContext` containing a `UsernamePasswordAuthenticationToken` and stores it in the `SecurityContextHolder` before the test, then clears it after. This listener is auto-registered when you use `@SpringBootTest`, `@WebMvcTest`, or otherwise import Spring's test context — you do **not** wire it manually. Key point: **no real authentication happens.** There is no password verification, no `AuthenticationManager` call, no HTTP request, no security filter chain. It's a shortcut that directly seeds the context. ## Attributes - `value` / `username` — the principal name. Default is `"user"`. - `password` — default `"password"` (rarely matters). - `roles` — e.g. `roles = {"ADMIN"}` becomes authority `ROLE_ADMIN`. Default is `{"USER"}` → `ROLE_USER`. **Spring auto-prefixes `ROLE_`; if you write `roles = "ROLE_ADMIN"` you get the illegal `ROLE_ROLE_ADMIN` and an exception.** - `authorities` — raw authorities with **no** prefix, e.g. `authorities = {"read", "ROLE_ADMIN"}`. You may set `roles` OR `authorities`, never both (throws). ## Scope / placement - On a **method** → applies to that test only. - On the **class** → applies to every test method (each can override with its own annotation). - On a **custom composed annotation** (meta-annotation) → build reusable `@WithMockAdmin`. ## Common gotchas 1. **CSRF**: `@WithMockUser` authenticates you but does NOT add a CSRF token. A `POST`/`PUT`/`DELETE` through MockMvc against a default (CSRF-enabled) config returns 403 unless you add `.with(csrf())`. 2. **roles vs authorities prefix confusion** — see above; the #1 mistake. 3. **It seeds the context, not an HTTP session** — good for method security and controllers alike, but it isn't testing your real login/filter path. To test the actual authentication flow you need a real request (e.g. `formLogin()` or `httpBasic()` post-processors), not `@WithMockUser`. 4. **Principal type** — the principal is a `User`/String, not your custom `UserDetails`. If your controller casts to a custom principal type, use `@WithUserDetails` or `user(...).password(...)` / a custom `@WithSecurityContext` instead. 5. **setupBefore** — `@WithMockUser(setupBefore = TestExecutionEvent.TEST_EXECUTION)` seeds the context *after* `@Before`/`@BeforeEach` methods instead of before them (the default is `TEST_METHOD`, i.e. before setup). Useful when setup code itself needs the user, or must NOT. ## When to use Default choice for controller/method-security tests where you just need *some* authenticated user with given roles and don't care about the exact `UserDetails` instance or a real login round-trip.

  • Why does a POST in a @WithMockUser test return 403 even though the user is authenticated?
    @WithMockUser only seeds authentication; it does not add a CSRF token. With Spring Security's default CSRF protection, mutating requests need a valid token, so you must add SecurityMockMvcRequestPostProcessors.csrf() (e.g. .with(csrf())).
  • What's the difference between roles = {"ADMIN"} and authorities = {"ADMIN"}?
    roles auto-prefixes ROLE_, giving authority ROLE_ADMIN (matches hasRole('ADMIN')). authorities is taken verbatim with no prefix, giving authority ADMIN (matches hasAuthority('ADMIN')). You can set one or the other, not both.

context