Testing Spring Boot Applications With MockMvc and @WebMvcTest

Last Updated:  May 23, 2023 | Published: September 28, 2020

Did you ever found yourself saying: I usually ignore testing my Spring Web MVC controller endpoints because the security setup is tricky. That belongs to the past. With MockMvc, Spring provides an excellent tool for testing Spring Boot applications. This guide provides you with recipes to verify your @Controller and @RestController endpoints and other relevant Spring Web MVC components using MockMvc.

Required Maven Dependencies For MockMvc

It is very little we need to start testing our controller endpoints with MockMvc.

We only need to include both the Spring Boot Starter Web and the Spring Boot Starter Test (aka. swiss-army for testing Spring Boot applications):

Once we secure our endpoints using Spring Security, the following test dependency is a must-have to test our protected controller with ease:

What Can We Test and Verify with MockMvc?

That's easy to answer: Everything related to Spring MVC!

This includes our controller endpoints (both @Controller and @RestController) and any Spring MVC infrastructure components like Filter, @ControllerAdvice, WebMvcConfigurer, etc.

Possible test scenarios can be the following:

  • Does my REST API controller return a proper JSON response?
  • Is the model for my Thymeleaf view initialized?
  • Does my endpoint return the HTTP status code 418 if my service layer throws a TeaNotFoundException?
  • Is my REST API endpoint protected with Basic Auth?
  • Can only users with the role ADMIN access the DELETE endpoint?
  • etc.

With MockMvc we perform requests against a mocked servlet environment. There won't be any real HTTP communication for such tests. We directly work with the mocked environment provided by Spring.

MockMvc acts as the entry point to this mocked servlet environment. Similar to the WebTestClient when accessing our started Servlet container over HTTP.

MockMvc Test Setup

There are two ways to create a MockMvc instance: using Spring Boot's auto-configuration or hand-crafting it.

Following Spring Boot's auto-configuration principle, we only need to annotate our test with @WebMvcTest. This annotation not only ensures to auto-configure MockMvc but also creates a sliced Spring context containing only MVC-related beans.

To keep the sliced test context small, we can pass the class name of the controller we want to test: @WebMvcTest(MyController.cass). Otherwise, Spring will create a context including all our controller endpoints.

The second approach is helpful if we're using Spring without Spring Boot or if we want to fine-tune the setup. The MockMvcBuilders class provides several entry points to construct a MockMvc instance:

or …

Invoke and Test an API Endpoint with MockMvc

Let's start with the first test: Ensuring the JSON result from a @RestController endpoint is correct.

Our sample controller has three endpoint mappings and acts as a REST API for a User entity:

With a first test, we want to ensure the JSON payload from /api/users is what we expect.

As our UserController has a dependency on a UserService bean, we'll mock it. This ensures we can solely focus on testing the web layer and don't have to provide further infrastructure for our service classes to work (e.g. remote systems, databases, etc.).

The minimal test setup looks like the following:

Before we invoke the endpoint using MockMvc, we have to mock the result of our UserService. Therefore we can return a hard-coded list of users:

Next, we use MockMvcRequestBuilders to construct our request against the mocked servlet environment. This allows us to specify the HTTP method, any HTTP headers, and the HTTP body.

Once we invoke our endpoint with perform, we can verify the HTTP response using fluent assertions to inspect: headers, status code, and the body.

JsonPath is quite helpful here to verify the API contract of our endpoint. Using its standardized expressions (somehow similar to XPath for XML) we can write assertions for any attribute of the HTTP response.

Our next test focuses on testing the HTTP POST endpoint to create new users. This time we need to send data alongside our MockMvc request:

To avoid an HTTP 403 Forbidden response, we have to populate a valid CsrfToken for the request. This only applies if your project includes Spring Security and CSRF is enabled (which you always should). Due to the great MockMvc and Spring Security integration, we can create this token using .with(csrf()) .

Writing Tests for a Thymeleaf Controller

There is more to Spring MVC than writing API endpoints: exposing server-side rendered views following the MVC (Model View Controller) pattern.

To showcase this kind of controller test, let's assume our application exposes one Thymeleaf view including a pre-filled Model:

From a testing perspective, it would be great if we can verify that our model is present and we are returning the correct view (locatable & renderable by Spring MVC). Writing a web test with Selenium for this scenario would be quite expensive (time & maintenance effort).

Fortunately, MockMvc provides verification mechanisms for these kinds of endpoints, too:

The MockMvc request setup looks similar to the tests in the last section. What's different is the way we assert the response. As this endpoint returns a view rather than JSON, we make use of the ResultMatcher .model().

And can now write assertions for the big M in MVC: the Model.

There is also a ResultMatcher available to ensure any FlashAttributues are present if you follow the POST – redirect – GET pattern.

Test a Secured Endpoint with Spring Security and MockMvc

Let's face reality, most of the time our endpoints are protected by Spring Security.  Neglecting to write tests because the security setup is hard, is foolish. Because it isn't. The excellent integration of MockMvc and Spring Security ensures this.

Once Spring Security is part of our project, the MockMvc will be auto-configured with our security config.

UPDATE: When using a SecurityFilterChain bean for the Spring Security config in Spring Boot 3.0, we need to manually import the security configuration with @Import(SecurityConfig.class) to our test.

As a demo, let's consider the following security configuration for our MVC application:

This configuration creates two kinds of endpoints: unprotected endpoints and protected endpoints using Basic Auth. Manually fiddling around with authentication is the last thing we want to do when verifying our secured Spring Web MVC endpoints.

Let's use the following API endpoint as an example:

Valid test scenarios would now include verifying that we block anonymous users, allow authenticated users access, and only allow privileged users to delete tasks.

The anonymous part is simple. Just invoke the endpoint without any further setup and expect HTTP status 401:

When we now want to test the happy path of creating a task, we need an authenticated user accessing the endpoint. Including the Spring Security Test dependency (see the first section), we have multiple ways to inject a user into the SecurityContext of the mocked Servlet environment.

The most simple one is user(), where we can specify any user-related attributes alongside the MockMvc request:

Adding this method to our MockMvc request setup, Spring Test populates a valid SecurityContext that holds information about the principal duke.

We can further tweak the user setup and assign roles to test the DELETE endpoint:

There are way more SecurityMockMvcRequestPostProcessors available that allow setting up users for your authentication method: jwt(), oauth2Login(), digest(), opaqueToken(), etc.

We can also use @WithMockUser on top of your test to define the mocked user for the whole test execution:

Using MockMvc in Combination with @SpringBootTest

We can also use MockMvc together with @SpringBootTest.

With this setup, we'll get our entire Spring application context populated and don't have to mock any service class.  Such tests ensure the integration of multiple parts of your application (aka. integration tests):

As with this setup, we still test against a mocked servlet environment, we should at least add some happy-path tests that invoke our application over HTTP. For this test scenario, the WebTestClient fits perfectly.

You can find the demo application for this testing Spring Boot MVC controllers example using MockMvc on GitHub.

PS: For more hands-on advice on testing real-world Spring Boot applications, consider enrolling for the Testing Spring Boot Applications Masterclass. As part of the Masterclass, we tackle testing Spring Boot endpoints with MockMvc in detail greater detail with a dedicated course module.

Joyful testing your Spring Boot application with MockMvc,

Philip

  • […] My application uses the Spring security configuration shown below, which requires a user to be authenticated. How can I mock a user for unit testing? It looks like I am being authenticated based on the content of MockHttpServletRequest; however, my test fails because I am denied access to the /portfolio endpoint and am instead redirected to the /login endpoint. I have followed the tutorial here: https://rieckpil.de/guide-to-testing-spring-boot-applications-with-mockmvc/ […]

  • I think there is an error in the test method shouldAllowDeletingReviewsWhenUserIsAdmin. The test pass even if you remove the roles. You must put the next annotation in the security configuration class:
    @EnableGlobalMethodSecurity(jsr250Enabled = true)

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
    >