Data Slice Tests with @DataJpaTest
Test repositories and queries against a real (test) database, with the rest of the app left out.
On this page
Your repositories carry real logic - derived queries, JPQL, JOIN FETCH. A mock can't
tell you whether findByAuthorIgnoreCaseOrderByTitleAsc actually returns the right rows
in the right order; only a database can. @DataJpaTest starts just the JPA layer
against a test database so you can verify queries for real, fast.
The slice
@DataJpaTest configures JPA, Hibernate, and your repositories - and nothing else
(no web layer, no services, no security). It gives each test a fresh, isolated database
and rolls back after every test, so tests never pollute each other.
@DataJpaTest
class BookRepositoryTest {
@Autowired BookRepository repository;
@Test
void findsBooksByAuthorSortedByTitle() {
repository.save(new Book("978-1", "Zebra", "Bloch", 1));
repository.save(new Book("978-2", "Apple", "Bloch", 1));
repository.save(new Book("978-3", "Other", "Gosling", 1));
var books = repository.findByAuthorIgnoreCaseOrderByTitleAsc("bloch");
assertThat(books).extracting(Book::getTitle).containsExactly("Apple", "Zebra");
}
}You saved real rows and ran a real query - the assertion proves the derived method's
WHERE, ORDER BY, and case-insensitivity all work.
Each test starts clean and rolls back
@DataJpaTest wraps each test in a transaction that's rolled back at the end, so tests are isolated and repeatable - you never have to clean up data between them.
H2 now, the real engine later
By default @DataJpaTest uses an embedded database (H2). That's fast and fine for
straightforward queries, but H2 isn't PostgreSQL - a native query or a database-specific
feature can pass on H2 and fail in production. That gap is exactly what Testcontainers
(next lesson) closes, by pointing the same slice at a real PostgreSQL container.
You can't verify a building's plumbing by reading the blueprint - you have to turn the
water on and watch it flow through the actual pipes. Mocking a repository tests the
blueprint; @DataJpaTest turns the water on. It runs your queries through a real JPA
provider and a real (if lightweight) database, so you find leaks the diagram can't show.
BookVault's LoanRepository.findActiveByMember uses a JPQL JOIN FETCH to load each
loan's book and member. Why can a plain unit test with a mocked repository never verify
this method works, and what would a @DataJpaTest prove that a mock cannot?
What does @DataJpaTest load and test?
Key takeaways
- @DataJpaTest starts only the JPA layer and repositories against a test database - no web, service, or security.
- It runs real queries against real rows, so it verifies derived methods, JPQL, ordering, and mappings a mock never could.
- Each test is wrapped in a transaction and rolled back, keeping tests isolated and repeatable.
- By default it uses embedded H2; database-specific behavior may differ from production.
- Point the same slice at a real engine with Testcontainers to close the H2-vs-production gap.