← Kursa Dön
📄 Text · 25 min

@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?

  • @Entity sınıfları

  • @Repository arayüzleri (Spring Data JPA)

  • EntityManager ve DataSource

  • Flyway/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.