Skip to content
Spring Boot sb testing 3 min read

@DataJpaTest

@DataJpaTest is a slice for testing the persistence layer. It auto-configures Spring Data JPA, your repositories, an embedded database, and a TestEntityManager — but leaves out web and service beans. Each test runs in a transaction that is rolled back at the end, so tests never pollute each other or the database.

What the slice loads

Annotate the test and inject the repository under test plus a TestEntityManager.

@DataJpaTest
class ProductRepositoryTest {

    @Autowired
    ProductRepository repository;

    @Autowired
    TestEntityManager entityManager;   // helper for arranging data
}

By default @DataJpaTest:

  • Configures Hibernate, Spring Data, and the DataSource.
  • Replaces your real database with an embedded one (H2/HSQLDB/Derby) if on the classpath.
  • Wraps each test in a transaction and rolls it back afterward.
  • Enables SQL logging so you can see the generated statements.

TestEntityManager

TestEntityManager is a test-friendly EntityManager. Use persist/persistAndFlush to arrange data without going through the repository you are testing, which keeps arrange and assert independent.

@Test
void findsByName() {
    entityManager.persistAndFlush(new Product("Keyboard", new BigDecimal("49.90")));
    entityManager.persistAndFlush(new Product("Mouse", new BigDecimal("19.90")));

    List<Product> found = repository.findByName("Keyboard");

    assertThat(found).hasSize(1);
    assertThat(found.get(0).getPrice()).isEqualByComparingTo("49.90");
}

Tip: persistAndFlush forces the INSERT immediately and clears the persistence-context cache for that entity, so a subsequent repository read genuinely hits the database rather than returning the cached instance.

Testing derived queries

Derived queries generate SQL from the method name. A @DataJpaTest is the perfect place to verify they return what you expect.

public interface ProductRepository extends JpaRepository<Product, Long> {
    List<Product> findByPriceGreaterThan(BigDecimal threshold);
    Optional<Product> findFirstByNameOrderByPriceAsc(String name);
}
@Test
void filtersByPrice() {
    entityManager.persist(new Product("Cheap", new BigDecimal("5.00")));
    entityManager.persist(new Product("Pricey", new BigDecimal("99.00")));
    entityManager.flush();

    List<Product> expensive = repository.findByPriceGreaterThan(new BigDecimal("50.00"));

    assertThat(expensive)
            .extracting(Product::getName)
            .containsExactly("Pricey");
}

Testing @Query methods

Custom JPQL or native queries deserve a test because the query string is not checked at compile time.

public interface ProductRepository extends JpaRepository<Product, Long> {

    @Query("select p from Product p where p.price between :min and :max")
    List<Product> inPriceRange(BigDecimal min, BigDecimal max);
}
@Test
void returnsProductsInRange() {
    entityManager.persist(new Product("A", new BigDecimal("10")));
    entityManager.persist(new Product("B", new BigDecimal("50")));
    entityManager.persist(new Product("C", new BigDecimal("90")));
    entityManager.flush();

    List<Product> result = repository.inPriceRange(new BigDecimal("20"), new BigDecimal("70"));

    assertThat(result).extracting(Product::getName).containsExactly("B");
}

Output:

Hibernate: select p1_0.id, p1_0.name, p1_0.price from product p1_0 where p1_0.price between ? and ?
ProductRepositoryTest > returnsProductsInRange() PASSED

Embedded vs real database

By default the slice swaps in an embedded database. That is fast, but H2 is not Postgres — dialect quirks, native queries, and DB-specific features can behave differently. To run against the real configured database, disable the replacement.

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class ProductRepositoryRealDbTest { }
Replace valueEffect
ANY (default)Always replace with an embedded DB
AUTO_CONFIGUREDReplace only auto-configured (not explicitly defined) DataSources
NONEUse the real configured DataSource — no replacement

Warning: Testing derived queries against H2 when production runs Postgres can give false confidence. For high-fidelity repository tests, set replace = NONE and provide a real Postgres via Testcontainers with @ServiceConnection.

Verifying rollback behavior

Each @DataJpaTest method is transactional and rolls back, so data written in one test is invisible to the next. If you need to assert the committed state, flush and clear, then re-read:

@Test
void persistsWithGeneratedId() {
    Product saved = repository.save(new Product("X", BigDecimal.ONE));
    entityManager.flush();
    entityManager.clear();   // drop first-level cache

    Product reloaded = repository.findById(saved.getId()).orElseThrow();
    assertThat(reloaded.getName()).isEqualTo("X");
}
Last updated June 13, 2026
Was this helpful?