How do `alwaysDo(...)` and `alwaysExpect(...)` on a MockMvc builder work, and when would you use them?
answer
- always* set on the builder, run every perform()
- alwaysDo -> ResultHandler (print/log everywhere)
- alwaysExpect -> ResultMatcher (shared invariant)
- augments, not replaces, per-request andDo/andExpect
- over-broad alwaysExpect breaks 204/error/non-JSON
basics
~10 sThey register a ResultHandler (alwaysDo) or ResultMatcher (alwaysExpect) once on the builder, and it runs automatically on every request performed by that MockMvc — so you don't repeat .andDo(...)/.andExpect(...) in each test.
solid answer
~40 sBoth are configured on a `ConfigurableMockMvcBuilder` (via `webAppContextSetup`/`standaloneSetup`) before `build()`. `alwaysDo(ResultHandler)` attaches a handler that runs on every `perform(...)` — commonly `alwaysDo(print())` or `alwaysDo(log())` so every request is dumped for debugging without repeating `.andDo(...)`. `alwaysExpect(ResultMatcher)` attaches an assertion that runs on every request — useful for invariants shared across a controller's tests, e.g. `alwaysExpect(content().contentType(MediaType.APPLICATION_JSON))` or a standard security header. You can register several; they run in addition to any per-request `.andDo(...)`/`.andExpect(...)`. Caveat: `alwaysExpect` must genuinely hold for *every* endpoint the MockMvc instance hits — error responses, 204 No Content, or non-JSON paths will break an over-broad global matcher, so keep global expectations to true universals.
code
java · 18 linesimport static org.springframework.test.web.servlet.result.MockMvcResultHandlers.log;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@BeforeEach
void setUp(WebApplicationContext context) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
.alwaysDo(log()) // every request dumped at DEBUG, no per-test .andDo
.alwaysExpect(content().contentType(MediaType.APPLICATION_JSON))
.build();
}
@Test
void getUser() throws Exception {
// content-type is asserted globally; only the specifics remain here
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("alice"));
}go deeper
Know they let you avoid repeating .andDo/.andExpect on every test.
Explain alwaysDo=ResultHandler, alwaysExpect=ResultMatcher, set on the builder.
Reason about which invariants are safe as globals and the 204/error-path pitfalls.
Set suite conventions (log() everywhere, minimal global matchers) and handle @AutoConfigureMockMvc via MockMvcBuilderCustomizer.
**Where they live.** `alwaysDo` and `alwaysExpect` are methods on `ConfigurableMockMvcBuilder` (the interface behind `MockMvcBuilders.webAppContextSetup(...)` and `standaloneSetup(...)`). You call them while configuring the builder, before `.build()`. They set defaults that apply to *every* request performed by the resulting `MockMvc`. **`alwaysDo(ResultHandler)`** — registers a `ResultHandler` (same type used by `.andDo(...)`) to run after every request. The canonical use is diagnostics: `alwaysDo(print())` or, better for committed suites, `alwaysDo(log())`, so you never sprinkle `.andDo(print())` across dozens of tests. You can register more than one; each is invoked per request. **`alwaysExpect(ResultMatcher)`** — registers a `ResultMatcher` (same type used by `.andExpect(...)`) to assert on every request. Use it for invariants that hold across an entire controller/test class: a shared response content type, a standard header (e.g. a security or cache header your filter always adds), or a common structural check. These run in addition to per-test `.andExpect(...)`. **Ordering / composition.** Global (`always*`) handlers and matchers run together with the per-request ones for each `perform`. Multiple globals run in registration order. They do not replace per-request expectations — they augment them. **Design guidance (why be careful).** - `alwaysExpect` is a footgun if the invariant is not truly universal. If one endpoint returns `204 No Content` (no body, no content type) or an error path returns `application/problem+json` while your global says `application/json`, the global matcher fails those tests spuriously. Restrict global matchers to genuine cross-cutting truths, or scope MockMvc instances per controller so the invariant really holds. - `alwaysDo(print())` on a large suite creates enormous, noisy output; prefer `alwaysDo(log())` (DEBUG-gated) so it is silent unless you flip the log level. - These are builder-scoped: they affect only that `MockMvc` instance. With `@AutoConfigureMockMvc`, you get a pre-built instance and cannot call `always*` on it directly — you'd customize via a `MockMvcBuilderCustomizer` bean, or build MockMvc manually to use them. **When to use.** - `alwaysDo(log())`: uniform, opt-in debugging across a test class. - `alwaysExpect(...)`: enforce a real, class-wide contract (content type, mandatory header) without repetition, when you are confident it holds for every request that instance makes.
- What is the risk of `alwaysExpect(content().contentType(APPLICATION_JSON))` across a whole app's MockMvc?Any endpoint that returns 204 No Content, a redirect, a non-JSON media type, or an error payload like application/problem+json will fail the global matcher. Scope it to controllers where JSON truly always applies.
- Can you call `alwaysDo`/`alwaysExpect` on the MockMvc injected by `@AutoConfigureMockMvc`?Not directly — those are builder methods and you receive a built instance. Register a MockMvcBuilderCustomizer bean, or build MockMvc manually via webAppContextSetup to use them.
saying these in an interview costs you the question
- Saying alwaysExpect replaces per-test andExpect (it augments them)
- Using alwaysExpect for non-universal invariants and blaming flakiness
- Calling always* on an already-built MockMvc instance
- Preferring alwaysDo(print()) over log() in large committed suites