Table of Contents
- Introduction to Integration Testing
- Difference Between Unit and Integration Testing
- Setting Up Integration Tests in Spring Boot
- Using
@SpringBootTest
for Full-Context Testing - Testing REST Controllers with
TestRestTemplate
andMockMvc
- Testing Repositories with Embedded Databases
- Using Testcontainers for Realistic Integration Tests
- Transaction Rollbacks and Test Isolation
- Data Initialization for Tests (
@Sql
,@DataJpaTest
,@TestEntityManager
) - Best Practices for Integration Testing
- Summary
1. Introduction to Integration Testing
Integration testing ensures that different layers or components of your application work together correctly. It verifies the behavior of the application in a more realistic runtime environment, where various components interact.
In Spring Boot, integration tests are used to:
- Test REST API endpoints from controller to database.
- Validate Spring configurations and beans.
- Simulate user behavior end-to-end.
2. Difference Between Unit and Integration Testing
Feature | Unit Testing | Integration Testing |
---|---|---|
Scope | Tests single class or method | Tests multiple components |
Speed | Very fast | Slower due to more setup |
Dependencies | Typically mocked | Use real components |
Frameworks | JUnit + Mockito | JUnit + Spring Boot Test, Testcontainers, etc. |
3. Setting Up Integration Tests in Spring Boot
Dependencies in Maven
xmlCopyEdit<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- For Testcontainers (optional) -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.1</version>
<scope>test</scope>
</dependency>
4. Using @SpringBootTest
for Full-Context Testing
The @SpringBootTest
annotation loads the full application context.
javaCopyEdit@SpringBootTest
class UserServiceIntegrationTest {
@Autowired
private UserService userService;
@Test
void testCreateUser() {
User user = userService.create("Alice");
assertNotNull(user.getId());
}
}
This test will initialize all Spring components, configurations, and data sources.
5. Testing REST Controllers with TestRestTemplate
and MockMvc
With TestRestTemplate
javaCopyEdit@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserControllerTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void testGetUser() {
ResponseEntity<User> response = restTemplate.getForEntity("/users/1", User.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
}
With MockMvc
javaCopyEdit@AutoConfigureMockMvc
@SpringBootTest
class UserControllerMockMvcTest {
@Autowired
private MockMvc mockMvc;
@Test
void testGetUser() throws Exception {
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("Alice"));
}
}
6. Testing Repositories with Embedded Databases
You can use @DataJpaTest
to test repositories with an in-memory database (like H2):
javaCopyEdit@DataJpaTest
class UserRepositoryTest {
@Autowired
private UserRepository repository;
@Test
void testFindById() {
User saved = repository.save(new User("Alice"));
Optional<User> found = repository.findById(saved.getId());
assertTrue(found.isPresent());
}
}
7. Using Testcontainers for Realistic Integration Tests
Instead of using H2, Testcontainers allows you to run real database instances (like PostgreSQL) in Docker containers for more production-like tests.
javaCopyEdit@Testcontainers
@SpringBootTest
class PostgreSQLContainerTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:14")
.withDatabaseName("testdb")
.withUsername("user")
.withPassword("pass");
@DynamicPropertySource
static void setDatasourceProps(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Test
void testDbInteraction() {
// use the real PostgreSQL instance
}
}
8. Transaction Rollbacks and Test Isolation
By default, Spring tests annotated with @Transactional
will roll back transactions after each test:
javaCopyEdit@Transactional
@Test
void testTransactionalRollback() {
userRepository.save(new User("Bob"));
// data won't persist after the test
}
This keeps tests isolated and database state clean.
9. Data Initialization for Tests
You can preload data using:
@Sql Annotation
javaCopyEdit@Sql("/test-data.sql")
@Test
void testWithPreloadedData() {
// test logic
}
@TestEntityManager (in @DataJpaTest)
javaCopyEdit@Autowired
private TestEntityManager entityManager;
@Test
void testUsingEntityManager() {
User user = new User("Carol");
entityManager.persist(user);
}
10. Best Practices for Integration Testing
- Use
@SpringBootTest
only when needed. Prefer@WebMvcTest
and@DataJpaTest
for lighter tests. - Reset the database state between tests to prevent data leaks.
- Use Testcontainers for a more realistic test environment.
- Keep your test environment configuration (like test DB credentials) separate from production.
- Avoid complex logic in tests—focus on expected behavior.
11. Summary
Integration testing in Spring Boot ensures your application components work together seamlessly. From REST endpoints to database operations, it validates the actual behavior of your application under realistic conditions. By combining tools like @SpringBootTest
, MockMvc
, TestRestTemplate
, and Testcontainers, you can build a robust and maintainable testing suite for your Spring Boot application.