Chapter 9.4☕ 16 min read

Integration Testing with @SpringBootTest

Unit tests check logic in isolation. But what if the JPA query you wrote is actually invalid SQL? A mock won't catch that. Integration tests start the real Spring Boot application to ensure all pieces (Controllers, Services, Repositories, and Database) fit together perfectly.

01The Concept: Full Application Context

The Ramoji Film City Set Walkthrough Analogy:

Unit testing is like testing a single fake sword in a prop room to see if it bends correctly.

Integration testing is bringing 500 actors onto the set, turning on the cameras, and shooting a scene. You are testing if the lighting, the cameras, the actors, and the set all work together.

@SpringBootTest boots up the entire Spring Application Context. It connects to a real database (usually an H2 in-memory database for tests) and runs actual SQL queries.

02Technical Explanation
  1. @SpringBootTest: Tells Spring Boot to start the full application context for this test.
  2. @AutoConfigureTestDatabase: Replaces your MySQL/PostgreSQL config with a fast, in-memory H2 database automatically, so you don't mess up your real data.
  3. @Transactional: If you add this to a test, any data inserted during the test is rolled back at the end, keeping your test database clean.
03Full Working Code: Testing the Repository Layer
package com.devinhyderabad;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

// 1. Start the full Spring Boot context
@SpringBootTest
// 2. Use an in-memory H2 database instead of real MySQL
@AutoConfigureTestDatabase
// 3. Rollback DB changes after the test finishes
@Transactional
public class BookRepositoryIntegrationTest {

@Autowired
private BookRepository bookRepository;

@Test
public void testFindByAuthor_ReturnsCorrectBooks() {
// 1. Arrange: Insert real data into the H2 database
bookRepository.save(new Book(1L, "Spring Guide", "Deva"));
bookRepository.save(new Book(2L, "Angular Guide", "Deva"));
bookRepository.save(new Book(3L, "Java Basics", "Sai"));

// 2. Act: Call the real Spring Data JPA method (Real SQL is generated!)
List<Book> devaBooks = bookRepository.findByAuthor("Deva");

// 3. Assert: Check the result
assertEquals(2, devaBooks.size());
}
}
04Why It Matters

Interview Question: "Why should you use @Transactional on integration tests in Spring Boot?"

Answer: By default, if a test method inserts data into the database, that data stays there after the test finishes. This can cause subsequent tests to fail because the database state is dirty. Adding @Transactional to the test class makes Spring automatically roll back the transaction at the end of each test method, ensuring a clean state for the next test.

05Interview Note

Enterprise Note: For testing real database constraints (like unique keys), @DataJpaTest is often preferred over @SpringBootTest because it only boots the JPA layer, making it much faster. But for testing the full flow from Controller to DB, @SpringBootTest is required.

Key Takeaways

  • ✅ @SpringBootTest starts the full application context for integration testing
  • ✅ @AutoConfigureTestDatabase replaces real DB with fast H2 in-memory DB
  • ✅ @Transactional on tests rolls back data automatically after each test
  • ✅ @DataJpaTest is a faster alternative for testing only the JPA/repository layer