@DataJpaTest ve Repository
@DataJpaTest, Spring Boot'un JPA katmanı için özel bir slice test anotasyonudur. Sadece JPA ile ilgili bileşenleri (Entity, Repository, EntityManager, DataSource) yükler — controller'lar, service'ler yüklenmez. Varsayılan olarak embedded veritabanı (H2) kullanır ve her test sonrasında rollback yapar.
@DataJpaTest Neyi Yükler?
@Entitysınıfları@Repositoryarayüzleri (Spring Data JPA)EntityManagerveDataSourceFlyway/Liquibase migration'ları (varsa)
Yüklemedikleri: @Service, @Controller, @Component, Security config
Temel Kullanım
@DataJpaTest
class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Autowired
private TestEntityManager entityManager;
@Test
@DisplayName("Kullanıcı kaydedilmeli ve ID atanmalı")
void shouldSaveUser() {
User user = User.builder()
.name("Ali Yılmaz")
.email("ali@test.com")
.password("encoded")
.role(Role.USER)
.active(true)
.build();
User saved = userRepository.save(user);
assertNotNull(saved.getId());
assertEquals("Ali Yılmaz", saved.getName());
}
@Test
@DisplayName("Email ile kullanıcı bulunmalı")
void shouldFindByEmail() {
// TestEntityManager ile doğrudan persist
User user = User.builder()
.name("Veli")
.email("veli@test.com")
.password("encoded")
.role(Role.USER)
.active(true)
.build();
entityManager.persistAndFlush(user);
Optional<User> found = userRepository.findByEmail("veli@test.com");
assertTrue(found.isPresent());
assertEquals("Veli", found.get().getName());
}
@Test
@DisplayName("Aktif kullanıcılar listelenmeli")
void shouldFindActiveUsers() {
entityManager.persistAndFlush(
User.builder().name("Ali").email("a@t.com")
.password("x").role(Role.USER).active(true).build());
entityManager.persistAndFlush(
User.builder().name("Veli").email("v@t.com")
.password("x").role(Role.USER).active(false).build());
entityManager.persistAndFlush(
User.builder().name("Zeynep").email("z@t.com")
.password("x").role(Role.USER).active(true).build());
List<User> activeUsers = userRepository.findByActiveTrue();
assertEquals(2, activeUsers.size());
assertTrue(activeUsers.stream()
.allMatch(User::isActive));
}
}TestEntityManager
TestEntityManager, JPA EntityManager'ın test dostu bir wrapper'ıdır:
@Autowired
private TestEntityManager entityManager;
// Kaydet ve flush et (veritabanına yaz)
User saved = entityManager.persistAndFlush(user);
// ID ile bul
User found = entityManager.find(User.class, 1L);
// Persistence context'i temizle (cache'i boşalt)
entityManager.clear();
// Detach — nesneyi yönetimden çıkar
entityManager.detach(user);
// persist + flush + clear = veritabanına yazıp cache'i temizle
// Bu sayede findById gerçekten veritabanından okur
entityManager.persistAndFlush(user);
entityManager.clear();
User fromDb = userRepository.findById(user.getId()).orElseThrow();entityManager.clear() özellikle önemlidir: Hibernate birinci seviye cache (persistence context) kullanır. Clear yapmazsanız, findById cache'den döner ve veritabanı sorgusunu test etmemiş olursunuz.
@Sql ile Test Verisi Yükleme
SQL dosyasından veri yüklemek için:
-- src/test/resources/test-data/users.sql
INSERT INTO users (name, email, password, role, active)
VALUES ('Ali', 'ali@test.com', 'encoded', 'USER', true);
INSERT INTO users (name, email, password, role, active)
VALUES ('Veli', 'veli@test.com', 'encoded', 'ADMIN', true);
INSERT INTO users (name, email, password, role, active)
VALUES ('Zeynep', 'zeynep@test.com', 'encoded', 'USER', false);@DataJpaTest
class UserRepositoryWithSqlTest {
@Autowired
private UserRepository userRepository;
@Test
@Sql("/test-data/users.sql")
void shouldLoadTestData() {
List<User> all = userRepository.findAll();
assertEquals(3, all.size());
}
@Test
@Sql(scripts = "/test-data/users.sql",
executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/test-data/cleanup.sql",
executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
void shouldCleanupAfterTest() {
// Test verisi yüklenir, test çalışır, sonra temizlenir
}
}Rollback Davranışı
@DataJpaTest varsayılan olarak @Transactional'dır — her test sonrası rollback yapılır:
@DataJpaTest
class RollbackDemoTest {
@Autowired
private UserRepository userRepository;
@Test
void test1_saveUser() {
userRepository.save(new User("Ali", "ali@test.com"));
assertEquals(1, userRepository.count());
}
@Test
void test2_databaseIsEmpty() {
// Önceki testin verileri rollback edildi
assertEquals(0, userRepository.count());
}
// Rollback'i devre dışı bırakmak (önerilmez):
@Test
@Rollback(false)
void test3_commitData() {
userRepository.save(new User("Veli", "veli@test.com"));
// Bu veri commit edilir — diğer testleri etkiler!
}
}Gerçek Veritabanı ile Test
Embedded DB yerine gerçek veritabanını kullanmak istiyorsanız:
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@ActiveProfiles("test")
class RealDatabaseTest {
// application-test.yml'deki gerçek veritabanı kullanılır
// Testcontainers ile birlikte kullanılması önerilir
}Custom Query Testleri
JPQL ve native query'lerin doğruluğunu test etmek:
// Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
@Query("SELECT p FROM Product p WHERE p.price BETWEEN :min AND :max ORDER BY p.price")
List<Product> findByPriceRange(@Param("min") double min, @Param("max") double max);
@Query(value = "SELECT * FROM products WHERE name ILIKE %:keyword%",
nativeQuery = true)
List<Product> searchByName(@Param("keyword") String keyword);
}
// Test
@DataJpaTest
class ProductRepositoryTest {
@Autowired
private ProductRepository productRepository;
@Autowired
private TestEntityManager em;
@BeforeEach
void setUp() {
em.persistAndFlush(new Product("Laptop", 999.99));
em.persistAndFlush(new Product("Mouse", 29.99));
em.persistAndFlush(new Product("Keyboard", 79.99));
em.persistAndFlush(new Product("Monitor", 499.99));
em.clear();
}
@Test
void shouldFindByPriceRange() {
List<Product> result = productRepository
.findByPriceRange(50.0, 500.0);
assertEquals(2, result.size());
assertEquals("Keyboard", result.get(0).getName());
assertEquals("Monitor", result.get(1).getName());
}
}@DataJpaTest, repository katmanını hızlı ve güvenilir bir şekilde test etmenin standart yoludur. Embedded veritabanı, otomatik rollback ve TestEntityManager ile her test izole çalışır ve custom query'lerinizin doğruluğundan emin olabilirsiniz.
AI Asistan
Sorularını yanıtlamaya hazır