@AutoConfigureMockMvc
Registers MockMvc and MockMvcTester in a @SpringBootTest, so requests run through the full application without starting a server.
@AutoConfigureMockMvc(addFilters = true)- Added in Spring Boot Spring Boot 1.4
Parameters
| Parameter | Type | Description |
|---|---|---|
$addFiltersoptional | boolean= true | Whether the context's servlet filters (including Spring Security) are added to MockMvc. Maps to spring.test.mockmvc.add-filters. |
$printoptional | MockMvcPrint= DEFAULT | Where the request/response dump goes: DEFAULT, LOG_DEBUG, SYSTEM_OUT, SYSTEM_ERR or NONE. |
$printOnlyOnFailureoptional | boolean= true | Print the dump only for failing tests. |
$htmlUnitoptional | AutoConfigureMockMvc.HtmlUnit= @HtmlUnit | HtmlUnit WebClient and WebDriver settings, for server-rendered pages. |
Return value
n/aRegisters MockMvc, and MockMvcTester when AssertJ is present, built from the test's WebApplicationContext.
Changelog
- PHP Spring Boot 4.0Moved to org.springframework.boot.webmvc.test.autoconfigure in the spring-boot-webmvc-test module.
@AutoConfigureMockMvc adds MockMvc support to a test's application context. It registers a MockMvc bean built from the full WebApplicationContext and, because AssertJ is on the test classpath, a MockMvcTester as well. Requests are dispatched straight into Spring MVC's DispatcherServlet on the test thread: no socket, no port, no real HTTP.
You need it with @SpringBootTest; @WebMvcTest already includes it. In Spring Boot 4 the import is org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc.
Why use it with the full context
@SpringBootTest plus @AutoConfigureMockMvc is the workhorse integration test. Every real bean is there, including services, repositories, transactions and security, yet the test is quicker than a RANDOM_PORT test and runs on the test thread. That last point matters: a @Transactional test class rolls back what the request wrote, so tests stay independent without clean-up code.
The trade-off is that the servlet container is not involved. Container-level behaviour such as the error page, response compression and server limits is not exercised. Keep a few RANDOM_PORT tests for those paths.
addFilters
addFilters (default true) adds the context's servlet filters to MockMvc, including Spring Security's filter chain. Setting it to false is a common way to make 401s disappear from tests, and a bad one. It also removes CORS, request logging and any other filters, so the tests pass against an application that does not exist in production. Authenticate the request instead with @WithMockUser or the request post-processors in spring-security-test.
Output when a test fails
print and printOnlyOnFailure control the request and response dump MockMvc writes. By default it is printed only when a test fails, which is what you want: green builds stay quiet and red ones show the body that came back. The same settings exist as spring.test.mockmvc.* properties, which suits setting them once in a test properties file.
RANDOM_PORT and MockMvc
Combining @AutoConfigureMockMvc with webEnvironment = RANDOM_PORT starts a server that MockMvc never talks to. Choose one: MockMvc against the mock environment, or an HTTP client such as RestTestClient against the real port.
Example
import static org.assertj.core.api.Assertions.assertThat;
import java.net.URI;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
class Ticket {
Long id;
String subject;
protected Ticket() {}
Ticket(String subject) {
this.subject = subject;
}
}
interface TicketRepository extends JpaRepository<Ticket, Long> {}
record NewTicket(String subject) {}
class TicketController {
private final TicketRepository tickets;
TicketController(TicketRepository tickets) {
this.tickets = tickets;
}
("/api/tickets")
ResponseEntity<Void> open( NewTicket request) {
Ticket saved = tickets.save(new Ticket(request.subject()));
return ResponseEntity.created(URI.create("/api/tickets/" + saved.id)).build();
}
}
// The whole application, driven through MockMvc on the test thread.
class TicketApiTest {
MockMvcTester mvc;
TicketRepository tickets;
void openingATicketStoresItAndReturnsItsLocation() {
mvc.post().uri("/api/tickets")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{"subject": "Printer on fire"}
""")
.assertThat()
.hasStatus(HttpStatus.CREATED)
.containsHeader("Location");
assertThat(tickets.count()).isEqualTo(1);
}
}
When to use it
- Post a new booking through the real controller, service and database and roll it back after the test.
- Check that every endpoint under /api/admin rejects a user without the ADMIN role.
- Print the response body in CI logs whenever an integration test fails.
Frequently asked questions
Why can't I autowire MockMvc in my @SpringBootTest?
@SpringBootTest does not register MockMvc by itself. Add @AutoConfigureMockMvc to the test class. With @WebMvcTest it is already included.
Is MockMvcTester available or only MockMvc?
Both. MockMvcTester is registered when AssertJ is on the test classpath, which spring-boot-starter-test provides.
Should I set addFilters = false to get rid of 401 responses?
No. It removes all servlet filters, including security, CORS and logging. Authenticate the test request with spring-security-test support such as @WithMockUser or with(jwt()) instead.
More examples
Testing security through the filter chain
With filters on, the real <code>SecurityFilterChain</code> runs, so the anonymous request gets 401 from HTTP Basic just as it would in production. <code>@WithMockUser</code> places an authenticated user in the security context for one test without needing a password or a user store. This pair of tests catches a rule accidentally changed to <code>permitAll()</code>.
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@Configuration
class SecurityConfig {
@Bean
SecurityFilterChain api(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults());
return http.build();
}
}
@RestController
class ReportController {
@GetMapping("/api/reports")
String reports() {
return "[]";
}
}
// addFilters defaults to true, so the security filter chain runs.
@SpringBootTest
@AutoConfigureMockMvc
class ReportSecurityTest {
@Autowired
MockMvcTester mvc;
@Test
void anonymousCallerIsRejected() {
mvc.get().uri("/api/reports")
.assertThat()
.hasStatus(HttpStatus.UNAUTHORIZED);
}
@Test
@WithMockUser(roles = "ANALYST")
void signedInUserGetsReports() {
mvc.get().uri("/api/reports")
.assertThat()
.hasStatusOk();
}
}
Configuring MockMvc output for the whole suite
These keys mirror the annotation's attributes, so they can be set once instead of on every class. Writing to standard out makes the failing request and response visible in CI logs, where a logger at debug level might be filtered out.
# src/test/resources/application.properties
# Print each request and response only when a MockMvc test fails (the default)...
spring.test.mockmvc.print-only-on-failure=true
# ...and send that output to standard out so CI logs show it.
spring.test.mockmvc.print=system-out
# Same switch as @AutoConfigureMockMvc(addFilters = ...). Leave it on.
spring.test.mockmvc.add-filters=true
The classic MockMvc API in the same context
Both <code>MockMvc</code> and <code>MockMvcTester</code> are registered, so older tests keep working while new ones use the AssertJ style. The classic API needs the static imports from <code>MockMvcRequestBuilders</code> and <code>MockMvcResultMatchers</code>, and every test method declares <code>throws Exception</code>.
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
record Version(String name, int build) {}
@RestController
class VersionController {
@GetMapping("/api/version")
Version version() {
return new Version("bookings-api", 128);
}
}
@SpringBootTest
@AutoConfigureMockMvc
class VersionEndpointTest {
// The classic API is registered alongside MockMvcTester.
@Autowired
MockMvc mockMvc;
@Test
void reportsTheBuild() throws Exception {
mockMvc.perform(get("/api/version"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("bookings-api"))
.andExpect(jsonPath("$.build").value(128));
}
}
Discussion