Testing the persistence layer
Write JPA tests that fail for the same reasons production would, not for H2 reasons.
Open this lesson in the learning hubKey points
@DataJpaTeststarts the JPA slice only, wraps each test method in a transaction and rolls it back on the way out.- That rollback is why tests do not leak into one another, and why you must never assert on rows a previous test left behind.
- The first level cache hides bugs.
flush()thenclear()before asserting, so the read really hits the database. - Test against the real engine with Testcontainers. H2 accepts SQL that Postgres rejects, and the dialects differ on locking and sequences.
- Add
@AutoConfigureTestDatabase(replace = Replace.NONE)or Spring quietly swaps your container for an in-memory database. - Assert the query count next to the result. Correctness and the number of round trips are two different bugs with two different fixes.
Example
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
class BookRepositoryTest {
@Container
@ServiceConnection // wires the datasource for you
static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16-alpine");
@Autowired TestEntityManager em;
@Autowired BookRepository books;
@Test
void findsByIsbnFromTheDatabaseNotTheCache() {
em.persist(new Book("978-0134685991", "Effective Java"));
em.flush();
em.clear(); // without this the L1 cache answers
Optional<Book> found = books.findByIsbn("978-0134685991");
assertThat(found).isPresent();
assertThat(found.get().getTitle()).isEqualTo("Effective Java");
}
// the transaction rolls back here, so the next test sees an empty table
}
Flush, clear, and use the real database - or you are testing the cache.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Hibernate course, and every lesson in it is listed on the Hibernate contents page.