Master MockMvc: Fast Server‑less Unit Testing for Spring Boot Controllers
Learn how to use MockMvc in Spring Boot to simulate HTTP requests, test controller logic without launching a server, and verify responses with concise code examples, covering dependency setup, controller creation, test class configuration, request execution, assertions, and common testing scenarios.
What is MockMvc?
MockMvc, part of the spring-test library, lets you simulate HTTP requests and invoke Spring MVC controllers directly, enabling fast unit tests that do not require a running server or network environment.
Why Use Mock Objects?
Mock objects replace real dependencies that are hard to create, slow, nondeterministic, or simply unavailable during testing. They allow you to focus on the code under test and verify expected outcomes.
Step‑by‑Step Guide to Using MockMvc in a Spring Boot Project
1. Add the Test Dependency
The default spring-boot-starter-test starter already pulls in spring-test, so no extra Maven coordinates are needed. For reference:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>2. Create a Simple Controller
@RestController
public class HelloWorldController {
@RequestMapping("/hello")
public String hello(String name) {
return "Hello " + name + "!";
}
}3. Write the Test Class
Configure the test with Spring’s test runner and set up MockMvc either via standaloneSetup or webAppContextSetup.
// For Spring Boot 1.4 and earlier use SpringJUnit4ClassRunner
@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class HelloWorldTest {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void setup() {
// Standalone mode – no full web context
mockMvc = MockMvcBuilders.standaloneSetup(new HelloWorldController()).build();
// Or use the full context
// mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
}4. Implement a Test Method
@Test
public void testHello() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/hello")
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
.param("name", "Tom"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("Hello Tom!"))
.andDo(MockMvcResultHandlers.print());
}The printed result looks like:
FlashMap: Attributes = null
MockHttpServletResponse:
Status = 200
Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"10"]
Body = Hello Tom!5. Overall Testing Workflow
Prepare the test environment.
Execute a request with MockMvc.
Add assertions to verify the outcome.
Optionally attach result handlers (e.g., printing).
Retrieve MvcResult for custom checks or asynchronous calls.
Tear down the test context.
Important Note
If you instantiate MockMvc with DefaultMockMvcBuilder, ensure the main application class scans the package containing your controllers, e.g.:
@ComponentScan(basePackages = "com.secbro2")Related APIs
RequestBuilder : builds MockHttpServletRequest objects; subclasses include MockHttpServletRequestBuilder and MockMultipartHttpServletRequestBuilder for file uploads.
MockMvcRequestBuilders : static factory methods such as get(), post(), etc.
ResultActions : returned by mockMvc.perform(); provides andExpect() (assertions), andDo() (result handlers), and andReturn() (raw MvcResult).
Common Test Scenarios
1. Test a Simple Controller
mockMvc.perform(get("/user/{id}", 1))
.andExpect(model().attributeExists("user"))
.andExpect(view().name("user/view"))
.andExpect(forwardedUrl("/WEB-INF/jsp/user/view.jsp"))
.andExpect(status().isOk())
.andDo(print());2. Obtain MvcResult for Custom Assertions
MvcResult result = mockMvc.perform(get("/user/{id}", 1))
.andReturn();
Assert.assertNotNull(result.getModelAndView().getModel().get("user"));3. Verify Parameter Binding and Flash Attributes
mockMvc.perform(post("/user").param("name", "zhang"))
.andExpect(handler().handlerType(UserController.class))
.andExpect(handler().methodName("create"))
.andExpect(model().hasNoErrors())
.andExpect(flash().attributeExists("success"))
.andExpect(view().name("redirect:/user"));4. File Upload
byte[] bytes = new byte[] {1, 2};
mockMvc.perform(fileUpload("/user/{id}/icon", 1L).file("icon", bytes))
.andExpect(model().attribute("icon", bytes))
.andExpect(view().name("success"));5. JSON Request/Response Validation
String requestBody = "{\"id\":1, \"name\":\"zhang\"}";
mockMvc.perform(post("/user")
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody)
.accept(MediaType.APPLICATION_JSON))
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.id").value(1));
String errorBody = "{id:1, name:zhang}";
MvcResult result = mockMvc.perform(post("/user")
.contentType(MediaType.APPLICATION_JSON)
.content(errorBody)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andReturn();
Assert.assertTrue(HttpMessageNotReadableException.class.isAssignableFrom(result.getResolvedException().getClass()));6. Asynchronous Testing
// Trigger async request
MvcResult asyncResult = mockMvc.perform(get("/user/async1?id=1&name=zhang"))
.andExpect(request().asyncStarted())
.andExpect(request().asyncResult(instanceOf(User.class)))
.andReturn();
// Dispatch the async result
mockMvc.perform(asyncDispatch(asyncResult))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.id").value(1));7. Global Configuration
mockMvc = webAppContextSetup(wac)
.defaultRequest(get("/user/1").requestAttr("default", true))
.alwaysDo(print())
.alwaysExpect(request().attribute("default", true))
.build();
mockMvc.perform(get("/user/1"))
.andExpect(model().attributeExists("user"));This guide provides a complete reference for using MockMvc to write effective, fast, and isolated unit tests for Spring Boot controllers.
Senior Brother's Insights
A public account focused on workplace, career growth, team management, and self-improvement. The author is the writer of books including 'SpringBoot Technology Insider' and 'Drools 8 Rule Engine: Core Technology and Practice'.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
