Spring MVC Testing: SpringBootTest vs WebMvcTest
When testing RESTful applications in Spring Boot, choosing between @SpringBootTest
and @WebMvcTest
is essential for efficient testing. These annotations serve different purposes: @SpringBootTest
loads the full application context, while @WebMvcTest
focuses solely on the web layer. This article will compare both approaches and help you understand when to use each one.
1. Using @SpringBootTest
with MockMvc
@SpringBootTest
is used for integration testing and loads the entire application context. When combined with @AutoConfigureMockMvc
, it enables the use of MockMvc
while loading all the beans in the context, including services, repositories, and other components.
Consider an application where users can perform CRUD operations through a REST API. If we want to test the behaviour of the entire application, from the web layer down to the database layer, @SpringBootTest
would be appropriate.
@AutoConfigureMockMvc @SpringBootTest public class UserApplicationTests { @Autowired private UserService userService; @Autowired private UserRepository userRepository; @Autowired private WebApplicationContext webApplicationContext; @Autowired private MockMvc mockMvc; @BeforeEach public void setUp() { mockMvc = MockMvcBuilders .webAppContextSetup(webApplicationContext) .build(); } @Test public void testCreateUser() throws Exception { Users user = new Users(); user.setName("Mr Fish"); user.setAge(25); mockMvc.perform(post("/api/users") .contentType(MediaType.APPLICATION_JSON) .content(asJsonString(user))) .andExpect(status().isOk()); Users savedUser = userRepository.findById(1L).orElse(null); assertNotNull(savedUser); assertEquals("Mr Fish", savedUser.getName()); assertEquals(25, savedUser.getAge()); } private static String asJsonString(final Object obj) { try { return new ObjectMapper().writeValueAsString(obj); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } }
In this example, @SpringBootTest
loads the complete application context, including components like UserService
, UserRepository
, and other infrastructure components. The MockMvc
object is employed to simulate HTTP requests to the REST API, allowing for the testing of the entire application stack. This test also ensures that the user is successfully saved in the database, demonstrating an integration test by verifying the interactions between the web, service, and database layers.
2. Using @WebMvcTest
with MockMvc
@WebMvcTest
is a Spring Boot annotation designed for slice testing, meaning it focuses solely on testing the MVC layer of an application. It limits the Spring context to web-related components, such as controllers, JSON conversion with Jackson or filters, ensuring that only the necessary components for handling web requests are loaded.
Now, let’s say we want to test only the controller layer and avoid dealing with the service or repository layers. In this scenario, @WebMvcTest
is a better choice. It focuses on testing the web layer in isolation.
@WebMvcTest(UserController.class) public class UserControllerTests { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @Test public void testGetUser() throws Exception { Users user = new Users(); user.setId(1L); user.setName("Fish"); user.setAge(25); when(userService.getUser(1L)).thenReturn(java.util.Optional.of(user)); mockMvc.perform(get("/api/users/1") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("Fish")) .andExpect(jsonPath("$.age").value(25)); } }
In this example, @WebMvcTest
is used to load only the web layer, specifically the UserController
, making it well-suited for unit testing controllers in isolation. The service layer is mocked using @MockBean
, allowing the test to focus solely on the controller’s logic without relying on the actual service implementation or database. MockMvc
is used to simulate HTTP requests, and assertions with jsonPath()
are employed to validate the response. This creates a more lightweight test, concentrating on the controller’s behaviour without involving other layers like the service or database.
3. Key Differences Between @WebMvcTest
and @SpringBootTest
3.1 The Degree of Integration
One of the main distinctions between @SpringBootTest
and @WebMvcTest
is the degree of integration testing they provide.
@SpringBootTest
loads the complete application context, which includes the database, security, and other infrastructure components. This makes it ideal for integration testing, where you want to verify how the various layers of your application (web, service, repository) interact with each other.@WebMvcTest
is specifically designed to test only the web layer, isolating the controller layer from the service and database layers. It’s more suited for unit testing controllers.
3.2 Configuration Differences
Another difference between @SpringBootTest
and @WebMvcTest
lies in their configuration requirements:
- @SpringBootTest: Since it loads the entire application context, we may need to configure additional components such as databases, security, and external services. This makes the tests more involved, but it allows us to validate the complete system.
- @WebMvcTest: This annotation requires less configuration because it focuses only on the web layer. We do not need to worry about database or service configurations, which simplifies writing and maintaining tests.
3.3 Comparison Summary
The following table summarizes the key distinctions between these two approaches.
Aspect | @SpringBootTest | @WebMvcTest |
---|---|---|
Scope of Testing | Full application context (web, service, DB) | Only the web layer (controllers) |
Use Case | Integration testing | Unit testing for controllers |
Service/Repository | Actual services and repositories used | Mocked services and repositories |
Test Complexity | More complex (requires additional setup) | Simpler (focuses only on the web layer) |
Performance | Slower (loads full context) | Faster (loads only web layer) |
Mocking | No need for mocks, actual beans are injected | Requires mocking other layers with @MockBean |
4. Conclusion
In this article, we explored the differences between using @SpringBootTest
and @WebMvcTest
with MockMvc
in testing Spring Boot applications. We examined how @SpringBootTest
loads the full application context, making it suitable for integration testing across multiple layers, while @WebMvcTest
focuses on testing the web layer in isolation, making it ideal for unit testing controllers.
5. Download the Source Code
This article focused on the differences between Spring MockMvc vs WebMvcTest.
You can download the full source code of this example here: spring mockmvc vs webmvctest
Hello, please, why do you autowire
@Autowired
private MockMvc mockMvc ;
if you are assigning it in setUp() method?
Hi Vladislav Stehno,
Thank you! You’re right about the code. Autowiring
MockMvc
and then reassigning it in thesetUp
method was an oversight on my part. The extra setup wasn’t necessary for this test, and the autowiredMockMvc
instance alone would suffice. I appreciate you pointing that out.Also,
UserService
andWebApplicationContext
dependencies must also be removed since they’re unused in this specific test.