Choosing Between MockMvc and @SpringBootTest for Controller Testing

Last Updated:  July 16, 2024 | Published: January 31, 2024

A common question we receive in the realm of testing Spring Boot application is about the differences between MockMvc and @SpringBootTest with webEnvironment=RANDOM_PORT for HTTP controller testing. Understanding why and when to use each approach is crucial for effective and efficient testing. Each has its unique benefits and use cases, which we'll explore through testing a simple GreetingController.

Testing Spring MVC Controller with MockMvc

MockMvc offers a lightweight and fast way to test this controller. MockMvc is part of spring-test and hence every Spring Boot project that contains the Spring Boot Starter Test (aka. the Testing Swiss-Army Knife) comes with it. When using the test slice annotation @WebMvcTest, Spring Boot will auto-configure an instance of MockMvc that we can inject into our test.

It's particularly adept at handling REST endpoints and views, integrating seamlessly with Spring Security. However, it's important to remember that this is a mocked environment, with no actual servlet container started.

Consider this MockMvc test example:

In the test example above, we're performing a request (not a real HTTP request) against the /greeting endpoint and expect the status code to be 200 and the body to contain the hard-coded string.

With MockMvc, no port will be occupied as it uses an in-memory mocked servlet environment. Hence, no servlet container (e.g., Tomcat, Jetty, Undertow) is started, which makes it extremely fast.

Overall, these are the benefits of using MockMvc:

  • Lightweight and Fast: MockMvc is exceptionally fast as it doesn’t start a full servlet container like Tomcat, Jetty, or Undertow. This makes it a go-to choice for rapid testing.
  • Seamless Integration with Spring Security: It easily integrates with Spring Security, allowing you to bypass or test various authentication and authorization setups.
  • Effective for REST Endpoints and Views: MockMvc integrates seamlessly with REST endpoints and views.
  • Fluent Checks: Offers numerous fluent checks for asserting the responses.
  • Mocked Servlet Environment: It's important to note that MockMvc creates a mocked servlet environment, meaning there's no real server and no port involved; interactions are done directly in-memory.

Testing with @SpringBootTest

@SpringBootTest with webEnvironment=RANDOM_PORT offers a more comprehensive testing environment, starting the entire application context and a real servlet container. This falls more under the integration test category as we're writing a test that interacts with multiple classes.

Here’s how to test the GreetingController using @SpringBootTest:

When running the test example above, we'll see the Spring Boot banner in our test logs, indicating that Spring starts in an application context. Depending on the size of our application and which actions are performed during the bootstrap phase, this may take some seconds. Once the servlet container starts successfully, our test will run and perform an HTTP request against our locally running application.

Thanks to Spring Boot, we can inject an already configured instance of TestRestTemplate that targets our locally running application. While this example is quite simple, writing such tests for projects with Spring Security setup requires more effort, as we can't fallback to methods like @WithMockUser here.

Overall, these are the benefits of using @SpringBootTest:

  • Full Application Startup: This approach starts the entire application, mimicking production scenarios more closely.
  • Real Servlet Container: It starts a real servlet container on a local port, allowing for HTTP mappings with Java classes.
  • Real HTTP Interaction: Tests are conducted using an HTTP client like WebTestClient or TestRestTemplate, hitting the application over real HTTP locally.
  • Security Setup Considerations: More effort is needed to handle security setups, such as attaching correctly signed JWTs or configuring security bypass.
  • Closer to Production Environment: This method provides a testing environment that is much closer to how the application will run in production.

Recommendation: When to Use Which Testing Approach

Choosing between MockMvc and @SpringBootTest depends on our specific needs. For quick, MVC-layer focused tests, MockMvc is the way to go. It's ideal for scenarios where you need to run a large number of tests with minimal setup. However, for thorough integration tests that require the full application context – perhaps to ensure all filters and converters are properly executed – @SpringBootTest with webEnvironment=RANDOM_PORT is more appropriate.

For sliced controller tests, covering various scenarios, MockMvc is usually our preference. It offers speed and precision without the overhead of a full application start.

Some final integration tests should hit the application from the outside. These tests don’t need to reiterate all scenarios (like non-authenticated users) but should ensure all servlet filters, converters, etc., are functioning as expected.

While there might still be minor differences, MockMvc can't catch potential configuration issues that might arise in a full application context.

Combining Both MockMvc and @SpringBootTest

Furthermore, @AutoConfigureMockMvc can be used together with @SpringBootTest for a hybrid approach. This enables testing within a full application context while using MockMvc for web layer interactions, offering a balanced mix of thoroughness and efficiency.

Combining @SpringBootTest with @AutoConfigureMockMvc allows for testing in a full Spring Boot application context while utilizing the convenience and speed of MockMvc for making requests. This hybrid approach is beneficial when you need to test the web layer in a more realistic environment that includes all the Spring Boot configurations and beans.

Here's how you can apply this combined approach to test the GreetingController:

In this setup, the test loads the full Spring Boot application context and uses MockMvc to send a request to the GreetingController. This approach ensures that all your configurations and beans are included in the test environment, while still retaining the ease and speed of using MockMvc for handling web requests. The servlet container is not started as we don't specify the webEnvironment property of @SpringBootTest and due to the default MOCK, no container is started.

What doesn't make sense is to configure MockMvc and using @SpringBootTest(webEnvironment=RANDOM_PORT) together. This setup would start both the servlet container and MockMvc, which is unnecessary and can lead to confusion and resource wastage:

Conclusion on MockMvc vs. @SpringBootTest

Understanding when to use MockMvc and @SpringBootTest with webEnvironment=RANDOM_PORT is key to efficient Spring Boot testing. MockMvc offers a fast, lightweight solution for MVC layer (REST controller or view endpoints) testing, while @SpringBootTest provides a more comprehensive, production-like environment.

For more in-depth coverage of this topic, including strategies to provide authentication and authorization when writing integration tests with Spring Boot, consider enrolling in the Testing Spring Boot Applications Masterclass.

Joyful testing,

Philip

>