|

Exploring the AssertJ Integration and New MockMvcTester Class in Spring Boot 3.4

Spring Boot 3.4 introduces AssertJ integration for tests and an exciting new class—MockMvcTester—designed to simplify and enhance testing of web controllers.

The AssertJ integration builds on top of plain MockMvc with several differences:

  • There is no need to use static imports as both the requests and assertions can be crafted using a fluent API.
  • Unresolved exceptions are handled consistently so that your tests do not need to throw (or catch) Exception.
  • By default, the result to assert is complete whether the processing is asynchronous or not. In other words, there is no need for special handling for Async requests.

It offers a modern, fluent API for asserting HTTP responses, making it easier to write clean and readable tests.

In this blog post, we will explore the AssertJ Support and MockMvcTester class, its features, and how to use it with examples.

What is MockMvcTester?

MockMvcTester is a new API in Spring Boot 3.4 that simplifies integration testing of Spring MVC controllers. It builds on the existing MockMvc framework but adds:

  • Fluent assertions for HTTP response properties.
  • Seamless JSON handling with support for extracting and validating JSON path values.
  • Improved readability with method chaining and expressive syntax.

Key Features:

  1. Fluent assertions for status codes, headers, and content types.
  2. JSON path extraction and validation.
  3. Easy integration with Java 8 streams and lambdas.
  4. Built-in support for HTTP methods and request setup.

Setup the Spring Boot Project

Go to start.spring.io and create a Spring Boot project by selecting version 3.4 or above

Create a Controller to Test

@RestController
@RequestMapping("/employee")
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    @GetMapping
    public List<Employee> getEmployees() {
        return employeeService.getAllEmployees();
    }

    @GetMapping(value="/{id}")
    public Employee getEmployeeById(@PathVariable Integer id) {
        return employeeService.getEmployeeById(id);
    }

    @PostMapping
    public Employee createEmployee(@RequestBody Employee employee) {
        return employeeService.createEmployee(employee);
    }

    @DeleteMapping(value="/{id}")
    public  void deleteEmployee(@PathVariable("id") Integer id){
        employeeService.deleteEmployee(id);
    }

}
Code language: Java (java)

Examples with MockMvcTester

Configuration

You could configure similar to MockMvc with @AutoConfigureMockMvc annotation

@SpringBootTest
@AutoConfigureMockMvc
public class EmpMockMvcTesterTest {
  @Autowired private MockMvcTester mockMvc;
}Code language: Java (java)

Below is another approach to configure MockMvcTester

@SpringBootTest
class EmployeeControllerTest {

    private final MockMvcTester mockMvc;

    @Autowired
    EmployeeControllerTest(WebApplicationContext context) {
        this.mockMvc = MockMvcTester.from(context);
    }
}Code language: Java (java)

Examples with MockMvcTester

1. Basic HTTP Assertions

@Test
public void testGetEmployeeById()  {
    MvcTestResult response = mockMvc.get().uri("/employee/100").exchange();
    assertThat(response)
            .hasStatusOk()
            .hasContentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE);        
}Code language: Java (java)

2. JSON Path Assertions

In below test I am using assertThatObject to verify the results. It saves you from type conversion of JsonPath reading.

@Test
public void testGetEmployeeById() throws UnsupportedEncodingException {
  MvcTestResult response = mockMvc.get().uri("/employee/100").exchange();
  assertThat(response)
      .hasStatusOk()
      .hasContentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE);

  var json = response.getResponse().getContentAsString();

  assertThatObject(JsonPath.read(json, "first_name")).isEqualTo("Alex");
  assertThatObject(JsonPath.read(json, "last_name")).isEqualTo("Bonilla");
  assertThatObject(JsonPath.read(json, "department.name")).isEqualTo("HR");
}Code language: Java (java)

If you want to use assertThat method to verify the results, you need to type cast the object like below.

assertThat((Integer) JsonPath.read(json, "id")).isEqualTo(100);
assertThat((String)JsonPath.read(json, "first_name")).isEqualTo("Alex");
assertThat((String)JsonPath.read(json, "last_name")).isEqualTo("Bonilla");
assertThat((String)JsonPath.read(json, "department.name")).isEqualTo("HR");
assertThat((String) JsonPath.read(json, "gender")).isEqualTo("M");Code language: Java (java)

Here are few different ways you can assert the single object results.

assertThat(mockMvc.get().uri("/employee/100"))
        .hasStatusOk()
        .hasContentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE)
        .bodyJson()
        .extractingPath("$")
        .asMap()
        .contains(entry("first_name", "Alex"), entry("last_name", "Bonilla"), entry("gender", "M"))
            .containsKey("department")
            .containsEntry("department", Map.of(
                    "id", 100,
                    "name", "HR"
            ));Code language: Java (java)
 assertThat(mockMvc.get().uri("/employee/100"))
        .hasStatusOk()
        .hasContentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE)
        .bodyJson()
        .extractingPath("$")
        .convertTo(Employee.class)
        .satisfies(e -> assertThat(e.getFirst_name()).isEqualTo("Alex"))
        .satisfies(e -> assertThat(e.getLast_name()).isEqualTo("Bonilla"));Code language: Java (java)

Post Request examples

MvcTestResult response =
    mockMvc
        .post()
        .uri("/employee")
        .contentType(MediaType.APPLICATION_JSON)
        .content(
            """
               {
                   "first_name": "John",
                   "last_name": "Doe",
                   "gender": "M",
                   "birth_date": "1990-12-07",
                   "hire_date": "2011-04-01"
               }
        """)
        .exchange();
assertThat(response.getResponse().getStatus()).isEqualTo(200);
assertThat(response.getResponse().getContentType()).isEqualTo(MediaType.APPLICATION_JSON_VALUE);

var json = response.getResponse().getContentAsString();
assertThatObject(JsonPath.read(json, "first_name")).isEqualTo("John");
assertThatObject(JsonPath.read(json, "last_name")).isEqualTo("Doe");
Code language: Java (java)
assertThat(
        mockMvc
            .post()
            .uri("/employee")
            .contentType(MediaType.APPLICATION_JSON)
            .content(
                """
                               {
                                   "first_name": "John",
                                   "last_name": "Doe",
                                   "gender": "M",
                                   "birth_date": "1990-12-07",
                                   "hire_date": "2011-04-01"
                               }
                        """))
    .hasStatusOk()
    .hasContentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE)
    .bodyJson()
    .extractingPath("$")
    .asMap()
    .contains(entry("first_name", "John"), entry("last_name", "Doe"), entry("gender", "M"));Code language: Java (java)
assertThat(
        mockMvc
                .post()
                .uri("/employee")
                .contentType(MediaType.APPLICATION_JSON)
                .content(
                        """
                           {
                               "first_name": "John",
                               "last_name": "Doe",
                               "gender": "M",
                               "birth_date": "1990-12-07",
                               "hire_date": "2011-04-01"
                           }
                        """))
        .hasStatusOk()
        .hasContentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE)
        .bodyJson()
        .convertTo(Employee.class)
        .satisfies(e -> assertThat(e.getFirst_name()).isEqualTo("John"))
        .satisfies(e -> assertThat(e.getLast_name()).isEqualTo("Doe"));Code language: Java (java)

4. Validating Lists and Counts

@Test
  public void testGetEmployees() throws UnsupportedEncodingException {
    MvcTestResult response = mockMvc.get().uri("/employee").exchange();
    assertThat(response)
            .hasStatusOk()
            .hasContentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE);

    // Parse JSON response as String
    var json = response.getResponse().getContentAsString();

    // Verify count of objects
    assertThat((Integer)JsonPath.read(json, "$.length()")).isEqualTo(8);

    // Verify first object values
    assertThatObject(JsonPath.read(json, "$[0].id")).isEqualTo(100);
    assertThat((String) JsonPath.read(json, "$[0].first_name")).isEqualTo("Alex");
    assertThat((String) JsonPath.read(json, "$[0].last_name")).isEqualTo("Bonilla");
    assertThat((String) JsonPath.read(json, "$[0].department.name")).isEqualTo("HR");

    // verify ids
    List<Integer> empIds = JsonPath.read(json, "$[*].id");
    assertThat(empIds).containsExactlyInAnyOrder(100, 200, 300, 400, 500, 600, 700, 800);

    // verify first names
    List<String> firstNames = JsonPath.read(json, "$[*].first_name");
    assertThat(firstNames)
        .containsExactlyInAnyOrder(
            "Alex", "Duke", "Hayley", "Nancie", "Wyatt", "Tobey", "Marvin", "Ibrar");
  }Code language: Java (java)

If you want to chain assertions

assertThat(mockMvc.get().uri("/employee"))
        .bodyJson()
        .extractingPath("$")
        .convertTo(InstanceOfAssertFactories.list(Employee.class))
        .hasSize(8)
        .satisfies(
            e ->
                assertThat(e.stream().map(Employee::getFirst_name).collect(Collectors.toList()))
                    .isEqualTo(
                        List.of(
                            "Alex", "Duke", "Hayley", "Nancie", "Wyatt", "Tobey", "Marvin",
                            "Ibrar")))
        .satisfies(
            e ->
                assertThat(
                        e.stream().map(Employee::getDepartment).toList().stream()
                            .map(Department::getName)
                            .collect(Collectors.toList()))
                    .containsExactlyInAnyOrder(
                        "HR", "PDE", "Marketing", "HR", "PDE", "Marketing", "HR", "PDE"));Code language: Java (java)

Using AssertJ With MockMvc Tests

You do not need use the MockMVcTester class in your tests to take advantages of AssertJ.

If you want to use the AssertJ support in MockMvc API, you can do that in following ways.

@SpringBootTest
@AutoConfigureMockMvc
public class EmpMockMvcAssertJTest {
  @Autowired private MockMvc mockMvc;

  @Test
  public void testGetEmployeeById() throws Exception {

    mockMvc
        .perform(get("/employee/100"))
        .andExpect(status().isOk())
        .andExpect(
            response -> {
              var json = response.getResponse().getContentAsString();
              assertThatObject(JsonPath.read(json, "first_name")).isEqualTo("Alex");
              assertThatObject(JsonPath.read(json, "last_name")).isEqualTo("Bonilla");
              assertThatObject(JsonPath.read(json, "department.name")).isEqualTo("HR");
              assertThat((String) JsonPath.read(json, "gender")).isEqualTo("M");
            });
  }
}Code language: Java (java)
 @Test
  public void testGetEmployeeById2() throws Exception {
    assertThat(
            mockMvc
                .perform(get("/employee/100"))
                .andExpect(status().isOk())
                .andReturn()
                .getResponse()
                .getContentAsString())
        .satisfies(
            json -> {
              assertThatObject(JsonPath.read(json, "first_name")).isEqualTo("Alex");
              assertThatObject(JsonPath.read(json, "last_name")).isEqualTo("Bonilla");
              assertThatObject(JsonPath.read(json, "department.name")).isEqualTo("HR");
              assertThat((String) JsonPath.read(json, "gender")).isEqualTo("M");
            });
  }Code language: Java (java)
@Test
  public void testGetEmployees() throws Exception {

    mockMvc
        .perform(get("/employee"))
        .andExpect(status().isOk())
        .andExpect(
            result -> {
              var json = result.getResponse().getContentAsString();

              List<Employee> employees = JsonPath.read(json, "$");
              assertThat(employees).hasSize(8);
              assertThat(employees)
                  .extracting("first_name")
                  .containsExactlyInAnyOrder(
                      "Alex", "Duke", "Hayley", "Nancie", "Wyatt", "Tobey", "Marvin", "Ibrar");
              //  assertThat(json)
              int employeeCount = JsonPath.read(json, "$.length()");
              assertThat(employeeCount).isEqualTo(8);
              assertThatObject(JsonPath.read(json, "$[0].id")).isEqualTo(100);

              // verify ids
              List<Integer> empIds = JsonPath.read(json, "$[*].id");
              assertThat(empIds).containsExactlyInAnyOrder(100, 200, 300, 400, 500, 600, 700, 800);

              // verify first names
              List<String> firstNames = JsonPath.read(json, "$[*].first_name");
              assertThat(firstNames)
                  .containsExactlyInAnyOrder(
                      "Alex", "Duke", "Hayley", "Nancie", "Wyatt", "Tobey", "Marvin", "Ibrar");
            });
  }Code language: Java (java)

Benefits of Using MockMvcTester

  1. Fluent API: The fluent API makes tests more readable and expressive.
  2. Reduced Boilerplate: Less code is required to set up and perform tests.
  3. Powerful Assertions: Built-in support for JSON Path, headers, and status codes.
  4. Seamless Integration: Works seamlessly with Spring Boot’s testing framework.

Conclusion

The MockMvcTester in Spring Boot 3.4 is a significant improvement for writing tests. It combines the best aspects of traditional MockMvc and modern fluent APIs like AssertJ, making it ideal for validating HTTP responses and JSON content in a clean, expressive manner.

Whether you’re starting a new project or updating existing tests, adopting MockMvcTester can streamline your testing strategy and improve code maintainability.

You can download the source code of blog post from GitHub

Similar Posts