Introduction to MockMvcTester

Spring Framework 6.2 introduced the new MockMvcTester API which allows us to write cleaner tests for Spring MVC controllers, and when we couple it with Spring Boot's auto-configuration support which was introduced in Spring Boot 3.4.0, configuring MockMvcTester couldn't be easier. This blog post provides a quick introduction to MockMvcTester and highlights its key features.

After we have finished this blog post, we:

  • Know what MockMvcTester is and understand why we should use it.
  • Can configure MockMvcTester.
  • Know how we can send HTTP requests to the system under test.
  • Understand how we can write assertions for the returned HTTP response.

Let's begin.

This blog post assumes that:

What and Why

MockMvcTester integrates MockMvc with AssertJ. It allows us to send HTTP requests to a Spring MVC controller method and write assertions for the returned response with AssertJ. MockMvcTester has these benefits over the pure MockMvc API:

1. Because MockMvcTester handles resolved exceptions, our tests don't have throw or catch Exception.

2. We don't have to use static imports because we can send HTTP requests to the system under test and specify our expectations (aka write assertions) by using a fluent API.

3. We can write tests for both asynchronous and synchronous request handling logic by using the same API.

4. It's a bit little easier to write comprehensive assertions for JSON documents. For example, if we have to ensure that the returned JSON document contains only the expected attributes (id and name) and we must use the "old" MockMvc API, we have two options:

First, we can write assertions which leverage JsonPath expressions. After we have written the required assertions, our assertion code looks as follows:

mockMvc.perform(
        //Build the request
)
        .andExpect(jsonPath("$").value(allOf(
                hasKey("id"),
                hasKey("name")
        )))
        .andExpect(jsonPath("$.*", hasSize(2)));

This doesn't look too bad, but the problem is that we have to write two test methods which ensure that the system under test:

  • Returns the correct attribute values.
  • Returns a JSON document that contains only the expected attributes.

Second, we can get the response body as a string and write the required assertion with JSONassert. After we have written the required assertion, our assertion code looks as follows:

String expectedJson = """
    {
        "id": 1,
        "name": "Test"
    }
""";

var actualResponseBody = mockMvc.perform(
        //Build the request
)
        .andReturn()
        .getResponse()
        .getContentAsString();
JSONAssert.assertEquals(expectedJson, actualResponseBody, true);

If we use JSONAssert, we have to write only one assertion which is a huge plus. However, this approach has three cons:

  • We must invoke three methods before we can get the asserted object (actual response body).
  • We must remember to pass the expected and actual response bodies in the correct order when we invoke the static assertEquals() method of the JSONAssert class.
  • We must remember to pass true as the last argument of the assertEquals() method if we want that the assertion uses strict mode and false if we want that the assertion uses lenient mode.
I think that the next parts of my MockMvcTester tutorial prove that MockMvcTester has an elegant API which ensures that writing assertions for JSON documents is a little bit easier.

Next, we will find out how we can configure MockMvcTester.

Configuring the MockMvcTester

When we configure MockMvcTester, we should use one of these three options:

1. If we are writing unit tests for a Spring or Spring Boot web application, we should follow these steps:

  1. Create a new test class.
  2. Add a mockMvcTester field to the test class and set the type of this field to MockMvcTester.
  3. Create a setup method that's run before a test method is run.
  4. Create a new MockMvc object by using the standalone configuration.
  5. Create a new MockMvcTester object by invoking the static create() method of the MockMvcTester class and pass the created MockMvc object as a method parameter. Store the created object in the mockMvcTester field.

After we have configured MockMvcTester, the source code of our test class looks as follows:

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

@DisplayName("MockMvcTester configuration example")
class MockMvcTesterTest {

    private MockMvcTester mockMvcTester;

    @BeforeEach
    void configureSystemUnderTest() {
        var mockMvc = MockMvcBuilders.standaloneSetup(new TestController())
                .build();
        mockMvcTester = MockMvcTester.create(mockMvc);
    }
}

2. If we are writing integration tests for a Spring web application, we should follow these steps:

  1. Create a new test class.
  2. Annotate the test class with the @SpringJUnitWebConfig annotation and configure the application context configuration class which configures the application context of our Spring web application. This annotation ensures that the Spring application context is loaded before our integration tests are run.
  3. Add a private and final mockMvcTester field to the test class and set the type of this field to MockMvcTester.
  4. Add a new constructor to the test class and ensure that the Spring container provides the loaded WebApplicationContext as a constructor argument.
  5. Create a new MockMvcTester object by invoking the static from() method of the MockMvcTester class and pass the WebApplicationContext object as a method parameter. Store the created object in the mockMvcTester field.

After we have configured MockMvcTester, the source code of our test class looks as follows:

import org.junit.jupiter.api.DisplayName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.web.context.WebApplicationContext;

@SpringJUnitWebConfig(ApplicationContextConfiguration.class)
@DisplayName("MockMvcTester configuration example")
class MockMvcTesterTest {

    private final MockMvcTester mockMvcTester;

    @Autowired
    MockMvcTesterTest(WebApplicationContext webApplicationContext) {
        this.mockMvcTester = MockMvcTester.from(webApplicationContext);
    }
}

3. If we are writing integration tests for a Spring Boot web application, we should follow these steps:

  1. Create a new test class.
  2. Annotate the test class with the @SpringBootTest annotation. This annotation ensures that the Spring application context is loaded before our integration tests are run.
  3. Annote the test class with the @AutoConfigureMockMvc annotation. This enables the auto-configuration of MockMvc. If the AssertJ library is found from the classpath, this annotation enables the auto-configuration of MockMvcTester as well.
  4. Add a private and final mockMvcTester field to the test class and set the type of this field to MockMvcTester.
  5. Inject the auto-configured MockMvcTester object into the mockMvcTester field by using constructor injection.

After we have configured MockMvcTester, the source code of our test class looks as follows:

import org.junit.jupiter.api.DisplayName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.assertj.MockMvcTester;

@SpringBootTest
@AutoConfigureMockMvc
@DisplayName("MockMvcTester configuration example")
class MockMvcTesterTest {

    private final MockMvcTester mockMvcTester;

    @Autowired
    MockMvcTesterTest(MockMvcTester mockMvcTester) {
        this.mockMvcTester = mockMvcTester;
    }
}

We can now configure MockMvcTester. Let's move on and find out how we can send HTTP requests to the system under test.

Sending HTTP Requests to the System Under Test

When we want to send an HTTP request to the system under test by using MockMvcTester, we have to follow these steps:

  1. Create a new request builder object which allows us to specify the target URI.
  2. Specify the target URI by using our request builder object.
  3. Set the other properties of the HTTP request (such as content type, cookies, headers, query and form parameters, request body, and so on).
  4. Send the HTTP request to the system under test and obtain the result object which allows us to write assertions for the returned HTTP response with AssertJ.
I use the terms: "HTTP request" and "HTTP response" in this tutorial because it makes things a bit easier to understand. You should remember that the MockMvc doesn't send real HTTP requests to the system under test. All tests are run in a mock environment provided by MockMvc.

Let's move on and take a closer look at these steps one by one.

1. When we want to create a new MockMvcRequestBuilder object, we can use the following methods of the MockMvcTester class: delete(), get(), head(), method() (pass the preferred HttpMethod as a method parameter), options(), patch(), post(), and put().

For example, if we want to create a request builder object for a GET request, our code looks as follows:

mockMvcTester.get()
//Specify the target URI
//Configure the other properties of the HTTP request
//Send the HTTP request and get the result object

On the other hand, we can also use the method() method of the MockMvcTester class. If we use this approach, our code looks as follows:

mockMvcTester.method(HttpMethod.GET)
//Specify the target URI
//Configure the other properties of the HTTP request
//Send the HTTP request and get the result object

2. When we want to specify the target URI, we have to invoke the uri() method of the AbstractMockHttpServletRequestBuilder class. The AbstractMockHttpServletRequestBuilder class has two uri() methods, which take different method parameters. These method parameters are:

  • A java.net.URI object which represents an absolute and fully constructed URI.
  • An URI template (a String object) and a sequence of objects which contain the URI variables.
We should use the latter method because it makes our tests easier to read.

Let's take a look at some examples which demonstrate how we can specify the target URI.

If we want to send a GET request to the path: /api/todo, our code looks as follows:

mockMvcTester.get()
        .uri("/api/todo")
//Configure the other properties of the HTTP request
//Send the HTTP request and get the result object

If we want to send a GET request to the path: /api/todo/1, our code looks as follows:

mockMvcTester.get()
        .uri("/api/todo/{id}", 1)
//Configure the other properties of the HTTP request
//Send the HTTP request and get the result object

If we want to specify query parameters by using the URI template style, our code looks as follows:

mockMvcTester.get()
        .uri("/api/todo?searchTerm={searchTerm}", "foo")
//Configure the other properties of the HTTP request
//Send the HTTP request and get the result object

3. After we have specified the target URI by invoking the uri() method, we can configure the other properties (such as content type, cookies, headers, query and form parameters, request body, and so on) of the HTTP request that's send to the system under test by using the request builder that's returned by the uri() method.

Let's take a look at some examples which demonstrate how we can use the returned request builder object.

If we want to specify the content type, we have to invoke the contentType() method of the AbstractMockHttpServletRequestBuilder class and pass a org.springframework.http.MediaType object as a method parameter. If we want to set the content type to: application/json, our code looks as follows:

mockMvcTester.post()
        .uri("/todo-item")
        .contentType(MediaType.APPLICATION_JSON)
//Send the HTTP request and get the result object

If we want to add a cookie to the HTTP request, we have to invoke the cookie() method of the AbstractMockHttpServletRequestBuilder class and pass a jakarta.servlet.http.Cookie object as a method parameter. If we want to add a cookie with name: CookieName and value: CookieValue to the HTTP request, our code looks as follows:

mockMvcTester.get()
        .uri("/api/todo")
        .cookie(new Cookie("CookieName", "CookieValue"))
//Send the HTTP request and get the result object

If we want to add a header to the HTTP request, we have to invoke the header() method of the AbstractMockHttpServletRequestBuilder class and pass these method parameters to the invoked method:

  1. The name of the header
  2. One or more header values

If we want to set the content type to: application/json by using the header() method, our code looks as follows:

mockMvcTester.post()
        .uri("/todo-item")
        .header("Content-Type", "application/json")
//Send the HTTP request and get the result object

If we want to add a query or a form parameter to the HTTP request, we have to invoke the param() method of the AbstractMockHttpServletRequestBuilder class and pass these method parameters to the invoked method:

  1. The name of the parameter
  2. One or more parameter values

If we want to add a query parameter: Foo with the value: Bar to the HTTP request, our code looks as follows:

mockMvcTester.post()
        .uri("/todo-item")
        .param("Foo", "Bar")
//Send the HTTP request and get the result object

If we want to add a form parameter: Foo with the value: Bar to the HTTP request, our code looks as follows:

mockMvcTester.post()
        .uri("/todo-item")
        .contentType(MediaType.APPLICATION_FORM_URLENCODED)
        .param("Foo", "Bar")
//Send the HTTP request and get the result object

If we want to specify the request body of the HTTP request, we have to invoke the content() method of the AbstractMockHttpServletRequestBuilder class and pass the request body as a method parameter. If we want to add a JSON document to the request body, our code looks as follows:

mockMvcTester.post()
        .uri("/todo-item")
        .contentType(MediaType.APPLICATION_JSON)
        .content("""
            {
                "description": "This is description",
                "name": "This is name"
            }
        """)
//Send the HTTP request and get the result object
When we specify the request body of the HTTP request, we must also set the content type of the HTTP request.

4. After we have created the HTTP request, we can send it to the system under test by invoking the exchange() method of the MockMvcRequestBuilder class. This method returns a MockMvcResult object which allows us to write assertions for the returned HTTP response with AssertJ. If we want to send an HTTP request to the system under test and store the result object in a local variable called: result, our code looks as follows:

var result = mockMvcTester.post()
        .uri("/todo-item")
        .contentType(MediaType.APPLICATION_JSON)
        .content("""
            {
                "description": "This is description",
                "name": "This is name"
            }
        """)
        .exchange();

We can now send HTTP requests to the system under test. Next, we will learn to write assertions for the returned HTTP response.

Writing Assertions

When we want to write assertions for the returned HTTP response, we have to:

  1. Invoke the static assertThat() method of the org.assertj.core.api.Assertions class and pass the MvcTestResult object as a method parameter.
  2. Write assertions for the returned HTTP response by using the fluent assertion API that's provided by the MvcTestResultAssert class.
We can pass the MvcTestResult object to the static assertThat() method because it implements the org.assertj.core.api.AssertProvider interface.

For example, if we have to write assertions for an HTTP response that's returned by the API endpoint which returns the information of one todo item, we can use one of these two options:

1. Store the response in a variable

var result = mockMvcTester.get()
        .uri("/todo-item/{id}", id)
        .exchange();
assertThat(result)
//Specify expectation

2. Pass the result to the assertThat() method

assertThat(
        mockMvcTester.get()
               .uri("/todo-item/{id}", id)
               .exchange()
)
//Specify expectation

We can specify our expectations by using the assertion methods provided by these classes:

  • The abstract AbstractHttpServletResponseAssert class provides AssertJ assertion methods for HttpServletResponse objects. These methods allow us to ensure that the system under test returns an HTTP response that has the correct content type, correct response headers, and correct HTTP status code.
  • The abstact AbstractMockHttpServletResponseAssert class extends the AbstractHttpServletResponseAssert class and provides AssertJ assertion methods for MockHttpServletResponse objects. The methods allow us to verify that the system under test returns an HTTP response that has the correct response body, contains the correct forwarded URL, contains the correct redirected URL, and contains the correct servlet error message.
  • The MvcTestResultAssert class extends the AbstractMockHttpServletResponseAssert class and provides AssertJ assertion methods for MvcTestResult objects. These methods allow us to ensure that the system under test returns an HTTP response that has correct cookies, sets the correct flash attributes, and returns the correct ModelAndView object. Also, this class provides methods which allow us to print the content of the returned MvcResult object.
Additional Reading:

There are two things I want to point out:

  • I think that we should use the second option because using an extra variable adds unnecessary noise to our test methods.
  • Because the required assertions depend on the tested controller method, I will cover the actual assertion methods in the next parts of my MockMvcTester tutorial.

We are now familiar with the basic features of MockMvcTester. Let's summarize what we learned from this blog post.

Summary

This blog post has taught us five things:

  • MockMvcTester integrates MockMvc with AssertJ.
  • MockMvcTester helps us to clean up our test code and write better assertions for JSON documents.
  • When we want to configure MockMvcTester, we can use the standalone configuration, create a new MockMvcTester object from the loaded Spring WebApplicationContext, or use the Spring Boot's auto-configuration support.
  • We can create an HTTP request and send it to the system under test by using the fluent API of the MockMvcRequestBuilder class.
  • When we want to write assertions for the returned HTTP response, we have to invoke the static assertThat() method of the org.assertj.core.api.Assertions class, pass the MvcTestResult object as a method parameter, and write assertions by using the fluent assertion API that's provided by the MvcTestResultAssert class.

P.S. You can get the example application from Github

3 comments… add one
  • MC Mar 1, 2025 @ 10:56

    Nicely written article! I love the step-by-step introduction of next configuration/usage options.

    • Petri Mar 1, 2025 @ 22:56

      Thank you for the feedback! I am happy to hear that this blog post was useful to you.

  • Balaji Rengan Apr 25, 2025 @ 20:27

    Giving example covering all possible scenarios is very good

Leave a Reply