Enterprise Java

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 TestingFull application context (web, service, DB)Only the web layer (controllers)
Use CaseIntegration testingUnit testing for controllers
Service/RepositoryActual services and repositories usedMocked services and repositories
Test ComplexityMore complex (requires additional setup)Simpler (focuses only on the web layer)
PerformanceSlower (loads full context)Faster (loads only web layer)
MockingNo need for mocks, actual beans are injectedRequires 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.

Download
You can download the full source code of this example here: spring mockmvc vs webmvctest

Omozegie Aziegbe

Omos Aziegbe is a technical writer and web/application developer with a BSc in Computer Science and Software Engineering from the University of Bedfordshire. Specializing in Java enterprise applications with the Jakarta EE framework, Omos also works with HTML5, CSS, and JavaScript for web development. As a freelance web developer, Omos combines technical expertise with research and writing on topics such as software engineering, programming, web application development, computer science, and technology.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Vladislav Stehno
Vladislav Stehno
1 month ago

Hello, please, why do you autowire
@Autowired
private MockMvc mockMvc ;
if you are assigning it in setUp() method?

Back to top button