Integration Testing in Spring Boot with JUnit 5 & MockMvc

In our previous tutorial, we built a Spring Boot CRUD REST API for User Management using Spring Data JPA and MySQL. We also wrote unit tests for the service and controller layers.

Now, it's time to perform Integration Testing, where we will test the full application flow from the controller to the database.

📌 Why Integration Testing?

✅ Ensures all layers (Controller, Service, Repository, Database) work together correctly.
✅ Verifies that Spring Boot components are properly wired.
✅ Helps identify unexpected errors in the system.
✅ Provides end-to-end API validation.

🚀 Step 1: Add Required Dependencies

📌 Ensure the following dependencies are present in pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
</dependency>

📌 Explanation

✔️ Spring Boot Test → Provides the testing framework for integration testing.
✔️ H2 Database → Used for in-memory database testing, preventing changes to the actual MySQL database.

🚀 Step 2: Configure an Integration Test Database

📌 Create a new configuration file for testing:
📌 src/test/resources/application-integrationtest.properties

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop

📌 Explanation

✔️ Uses H2 in-memory database, so tests don’t modify MySQL data.
✔️ ddl-auto=create-drop → Automatically creates and drops tables after tests.

🚀 Step 3: Create the Integration Test Class

📌 Create UserControllerIntegrationTest.java inside net.javaguides.usermanagement.controller

package net.javaguides.usermanagement.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import net.javaguides.usermanagement.dto.UserDto;
import net.javaguides.usermanagement.entity.User;
import net.javaguides.usermanagement.repository.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
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.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import java.time.LocalDate;

import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest(properties = "spring.config.location=classpath:application-integrationtest.properties")
@AutoConfigureMockMvc
public class UserControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ObjectMapper objectMapper;

    @BeforeEach
    void setUp() {
        userRepository.deleteAll(); // Clean database before each test
    }
}

📌 Explanation

✔️ @SpringBootTest → Loads the full application context for testing.
✔️ @AutoConfigureMockMvc → Enables MockMvc for API testing.
✔️ userRepository.deleteAll() → Clears the database before each test.

🚀 Step 4: Test POST /api/users (Create User)

📌 Add the following test inside UserControllerIntegrationTest.java

@Test
void shouldCreateUser() throws Exception {
    // Given
    UserDto userDto = new UserDto(
            null,
            "Ravi",
            "Kumar",
            "ravi.kumar@example.com",
            LocalDate.of(1995, 8, 15)
    );

    // When & Then
    mockMvc.perform(post("/api/users")
            .contentType(MediaType.APPLICATION_JSON)
            .content(objectMapper.writeValueAsString(userDto)))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.id", notNullValue()))
            .andExpect(jsonPath("$.firstName", is("Ravi")))
            .andExpect(jsonPath("$.email", is("ravi.kumar@example.com")));
}

📌 Explanation

✔️ Sends a POST request with user JSON data.
✔️ Verifies response status (200 OK) and response body.

🚀 Step 5: Test GET /api/users/{id} (Retrieve User)

📌 Add the following test inside UserControllerIntegrationTest.java

@Test
void shouldGetUserById() throws Exception {
    // Given
    User user = new User();
    user.setFirstName("Ravi");
    user.setLastName("Kumar");
    user.setEmail("ravi.kumar@example.com");
    user.setDateOfBirth(LocalDate.of(1995, 8, 15));
    user = userRepository.save(user);

    // When & Then
    mockMvc.perform(get("/api/users/{id}", user.getId())
            .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.id", is(user.getId().intValue())))
            .andExpect(jsonPath("$.firstName", is("Ravi")))
            .andExpect(jsonPath("$.email", is("ravi.kumar@example.com")));
}

🚀 Step 6: Test GET /api/users (Retrieve All Users)

📌 Add the following test inside UserControllerIntegrationTest.java

@Test
void shouldGetAllUsers() throws Exception {
    // Given
    User user1 = new User();
    user1.setFirstName("Ravi");
    user1.setLastName("Kumar");
    user1.setEmail("ravi.kumar@example.com");
    user1.setDateOfBirth(LocalDate.of(1995, 8, 15));

    User user2 = new User();
    user2.setFirstName("Priya");
    user2.setLastName("Sharma");
    user2.setEmail("priya.sharma@example.com");
    user2.setDateOfBirth(LocalDate.of(1992, 3, 10));

    userRepository.save(user1);
    userRepository.save(user2);

    // When & Then
    mockMvc.perform(get("/api/users")
            .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.size()", is(2)))
            .andExpect(jsonPath("$[0].firstName", is("Ravi")))
            .andExpect(jsonPath("$[1].firstName", is("Priya")));
}

🚀 Step 7: Test PUT /api/users/{id} (Update User)

📌 Add the following test inside UserControllerIntegrationTest.java

@Test
void shouldUpdateUser() throws Exception {
    // Given
    User user = new User();
    user.setFirstName("Ravi");
    user.setLastName("Kumar");
    user.setEmail("ravi.kumar@example.com");
    user.setDateOfBirth(LocalDate.of(1995, 8, 15));
    user = userRepository.save(user);

    UserDto updatedUserDto = new UserDto(
            user.getId(),
            "UpdatedName",
            "Kumar",
            "updated.email@example.com",
            LocalDate.of(1995, 8, 15)
    );

    // When & Then
    mockMvc.perform(put("/api/users/{id}", user.getId())
            .contentType(MediaType.APPLICATION_JSON)
            .content(objectMapper.writeValueAsString(updatedUserDto)))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.firstName", is("UpdatedName")))
            .andExpect(jsonPath("$.email", is("updated.email@example.com")));
}

🚀 Running the Tests

📌 Execute all the JUnit test cases by running the UserControllerIntegrationTest class.

✅ All tests should pass successfully.
Integration Testing in Spring Boot with JUnit 5 & MockMvc

🎯 Summary: What We Achieved

✔️ Written Integration JUnit tests to CRUD REST APIs
✔️ Tested REST APIs with full integration.
✔️ Validated API responses & database operations.

🚀 Now your Spring Boot Application is fully tested with Integration Tests! 🎯

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare