@DataJpaTest
IntermediateLoads only the JPA layer, auto-configures an in-memory DB, and rolls back each test; ideal for testing repository query correctness in isolation.
Overview
@DataJpaTest is a Spring Boot test slice for the JPA layer: it loads entities, repositories, @Converters, and Hibernate configuration but not controllers, services, or @Component beans. By default it replaces the configured DataSource with an embedded H2 in-memory database and wraps each test in a transaction that rolls back after the test, leaving no state behind. This makes tests fast and deterministic. For tests that need a real database engine (to test MySQL-specific features, window functions, or migration correctness), replace H2 with Testcontainers by adding @AutoConfigureTestDatabase(replace = NONE) and providing a real DataSource.
Basic @DataJpaTest repository test
Test custom @Query methods and derived queries against an H2 in-memory database. TestEntityManager is injected for setting up test data without going through repositories.
@DataJpaTest // loads only JPA slice; H2 replaces configured DB
class OrderRepositoryTest {
@Autowired
private TestEntityManager em;
@Autowired
private OrderRepository orderRepository;
@Test
void findByStatus_returnsPendingOrders() {
// Arrange: persist test data via TestEntityManager
Customer customer = em.persist(new Customer("john@example.com"));
em.persist(new Order(customer, "PENDING", new BigDecimal("50.00")));
em.persist(new Order(customer, "SHIPPED", new BigDecimal("30.00")));
em.persist(new Order(customer, "PENDING", new BigDecimal("20.00")));
em.flush();
// Act
List<Order> result = orderRepository.findByStatus("PENDING");
// Assert
assertThat(result).hasSize(2)
.extracting(Order::getStatus)
.containsOnly("PENDING");
}
@Test
void findTopSpenders_returnsCustomersSortedByRevenue() {
// Test a native query with aggregation
// ...
List<CustomerRevenue> top = orderRepository.findTopSpenders(
PageRequest.of(0, 5));
assertThat(top).isSortedAccordingTo(
Comparator.comparing(CustomerRevenue::getTotalRevenue).reversed());
}
}@DataJpaTest with Testcontainers (real MySQL)
Replace H2 with a real MySQL container when tests need database-specific behaviour: window functions, JSON functions, or Flyway migration correctness.
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
class OrderRepositoryMySQLTest {
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.3")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", mysql::getJdbcUrl);
registry.add("spring.datasource.username", mysql::getUsername);
registry.add("spring.datasource.password", mysql::getPassword);
registry.add("spring.jpa.hibernate.ddl-auto", () -> "create-drop");
// Or use Flyway: spring.flyway.enabled=true
}
@Autowired
private OrderRepository orderRepository;
@Test
void windowFunctionQuery_ranksOrdersCorrectly() {
// This test requires MySQL window functions — fails on H2
List<OrderRank> ranked = orderRepository.rankOrdersByValue();
assertThat(ranked.get(0).getRank()).isEqualTo(1);
}
}Testing auditing and custom converters
@DataJpaTest includes @Converters and auditing infrastructure. Test that @CreatedDate, @LastModifiedDate, and custom AttributeConverters work correctly.
@DataJpaTest
@Import(JpaConfig.class) // import @EnableJpaAuditing config
class AuditingTest {
@Autowired
private TestEntityManager em;
@Autowired
private OrderRepository orderRepository;
@Test
void createdAt_isSetOnPersist() {
Order order = new Order("cust-1", "PENDING", BigDecimal.TEN);
Order saved = orderRepository.saveAndFlush(order);
assertThat(saved.getCreatedAt()).isNotNull();
assertThat(saved.getUpdatedAt()).isNotNull();
assertThat(saved.getCreatedAt()).isBeforeOrEqualTo(saved.getUpdatedAt());
}
@Test
void updatedAt_changesOnUpdate() throws InterruptedException {
Order order = orderRepository.saveAndFlush(
new Order("cust-1", "PENDING", BigDecimal.TEN));
LocalDateTime original = order.getUpdatedAt();
Thread.sleep(10);
order.setStatus("CONFIRMED");
Order updated = orderRepository.saveAndFlush(order);
assertThat(updated.getUpdatedAt()).isAfter(original);
}
}Key Points to Remember
- 1@DataJpaTest loads JPA layer only — no controllers, no services, no @Component beans.
- 2Each test runs in a transaction that rolls back automatically — no teardown required.
- 3H2 in-memory database is used by default; add @AutoConfigureTestDatabase(replace=NONE) to use a real DB.
- 4TestEntityManager wraps EntityManager with helpers like persist(), flush(), find() useful for test setup.
- 5Use Testcontainers for MySQL-specific features (JSON functions, window functions, Flyway migrations).
- 6Import @EnableJpaAuditing configuration explicitly if it is not auto-applied in the slice.
Interview Questions
Sign in to ask AriaWhat does @DataJpaTest load and what does it exclude?
Why might a test pass with H2 but fail with MySQL in a @DataJpaTest?
How do you use Testcontainers with @DataJpaTest to test against a real database?
What is the difference between TestEntityManager and a regular repository in @DataJpaTest?
How do you test that a custom @Query method returns the correct pagination metadata?
Ask Aria about @DataJpaTest
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.