← Назад к вопросам

Как протестировать базу данных

2.2 Middle🔥 231 комментариев
#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Как протестировать базу данных в Java

Тестирование БД — критически важная часть разработки. Покажу несколько подходов от простого к сложному.

Способ 1: Unit тесты с H2 in-memory БД

H2 — быстрая в памяти БД, идеальна для быстрых тестов.

Зависимости

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>  <!-- Только для тестов!
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Конфиг для тестов

# src/test/resources/application-test.yml
spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password:
  
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    hibernate:
      ddl-auto: create-drop  # Создаёт и удаляет schema для каждого теста
    show-sql: true

Тест

import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.TestPropertySource;

@DataJpaTest
@TestPropertySource(locations = "classpath:application-test.yml")
public class UserRepositoryTest {
    
    @Autowired
    private UserRepository userRepository;  // Spring автоматически инъектирует
    
    @Test
    void testSaveUser() {
        // ARRANGE
        User user = new User();
        user.setName("John");
        user.setEmail("john@example.com");
        
        // ACT
        User saved = userRepository.save(user);
        
        // ASSERT
        assertNotNull(saved.getId());
        assertEquals("John", saved.getName());
    }
    
    @Test
    void testFindByEmail() {
        // ARRANGE
        User user = new User();
        user.setName("Jane");
        user.setEmail("jane@example.com");
        userRepository.save(user);
        
        // ACT
        Optional<User> found = userRepository.findByEmail("jane@example.com");
        
        // ASSERT
        assertTrue(found.isPresent());
        assertEquals("Jane", found.get().getName());
    }
    
    @Test
    void testFindByEmailNotFound() {
        // ACT
        Optional<User> found = userRepository.findByEmail("nonexistent@example.com");
        
        // ASSERT
        assertFalse(found.isPresent());
    }
}

Способ 2: Интеграционные тесты с TestContainers

TestContainers позволяет запускать реальную БД в Docker контейнере.

Зависимости

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.19.0</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <version>1.19.0</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-testcontainers</artifactId>
    <scope>test</scope>
</dependency>

Тест с PostgreSQL

import org.springframework.boot.test.context.SpringBootTest;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
@SpringBootTest
public class UserRepositoryIntegrationTest {
    
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
        .withDatabaseName("testdb")
        .withUsername("testuser")
        .withPassword("testpass");
    
    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void testSaveUserWithPostgres() {
        User user = new User();
        user.setName("Alice");
        user.setEmail("alice@example.com");
        
        User saved = userRepository.save(user);
        
        assertNotNull(saved.getId());
        assertEquals("Alice", saved.getName());
    }
    
    @Test
    void testComplexQuery() {
        // Тестируем сложные запросы с настоящей БД
        userRepository.save(new User("Alice", "alice@example.com"));
        userRepository.save(new User("Bob", "bob@example.com"));
        userRepository.save(new User("Charlie", "charlie@example.com"));
        
        List<User> users = userRepository.findAll();
        
        assertEquals(3, users.size());
    }
}

Способ 3: Сквозное тестирование (E2E) с @SpringBootTest

@SpringBootTest
@Transactional  // Откатывает изменения после каждого теста
public class OrderServiceIntegrationTest {
    
    @Autowired
    private OrderService orderService;
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private PaymentRepository paymentRepository;
    
    @Test
    void testCreateOrderEndToEnd() {
        // Тестируем весь путь: сервис -> репозиторий -> БД
        Order order = new Order();
        order.setCustomerId(1L);
        order.setAmount(BigDecimal.valueOf(100.0));
        
        orderService.createOrder(order);
        
        // Проверяем что записалось в БД
        Optional<Order> saved = orderRepository.findById(order.getId());
        assertTrue(saved.isPresent());
        assertEquals(1L, saved.get().getCustomerId());
    }
}

Способ 4: Тестирование native queries

@DataJpaTest
public class NativeQueryTest {
    
    @Autowired
    private EntityManager entityManager;
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    @Transactional
    void testNativeQuery() {
        // Сохраняем тестовые данные
        User user = new User();
        user.setName("TestUser");
        user.setEmail("test@example.com");
        userRepository.save(user);
        
        // Выполняем native query
        @SuppressWarnings("unchecked")
        List<User> results = (List<User>) entityManager
            .createNativeQuery("SELECT * FROM users WHERE email = ?1", User.class)
            .setParameter(1, "test@example.com")
            .getResultList();
        
        assertFalse(results.isEmpty());
        assertEquals("TestUser", results.get(0).getName());
    }
}

Способ 5: Тестирование с миграциями (Flyway)

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestPropertySource("classpath:application-test.yml")
public class MigrationTest {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Test
    void testMigrationApplied() {
        // Проверяем что таблица создана
        String sql = "SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'users'";
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
        
        assertEquals(1, count);
    }
}

Способ 6: Fixture и test data

@DataJpaTest
public class UserRepositoryWithFixturesTest {
    
    @Autowired
    private UserRepository userRepository;
    
    // Fixture — подготовка данных
    @BeforeEach
    void setUp() {
        userRepository.save(new User("Alice", "alice@example.com"));
        userRepository.save(new User("Bob", "bob@example.com"));
        userRepository.save(new User("Charlie", "charlie@example.com"));
    }
    
    @Test
    void testFindAllUsers() {
        List<User> users = userRepository.findAll();
        
        assertEquals(3, users.size());
    }
    
    @Test
    void testDeleteUser() {
        User alice = userRepository.findByEmail("alice@example.com").get();
        userRepository.delete(alice);
        
        assertEquals(2, userRepository.count());
    }
}

Способ 7: Специальные аннотации для БД

// Кастомная аннотация для повторного использования конфигурации
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@DataJpaTest
@TestPropertySource("classpath:application-test.yml")
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
public @interface RepositoryTest { }

// Использование
@RepositoryTest
public class CustomUserRepositoryTest {
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void test() { }
}

Способ 8: Тестирование с несколькими БД (Polyglot)

@SpringBootTest
@Testcontainers
public class PolyglotDatabaseTest {
    
    @Container
    static PostgreSQLContainer<?> postgres = 
        new PostgreSQLContainer<>("postgres:15");
    
    @Container
    static GenericContainer<?> redis = 
        new GenericContainer<>("redis:7").withExposedPorts(6379);
    
    @DynamicPropertySource
    static void properties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.redis.host", redis::getHost);
        registry.add("spring.redis.port", redis::getFirstMappedPort);
    }
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private RedisTemplate<String, User> redisTemplate;
    
    @Test
    void testPersistenceAndCache() {
        User user = userRepository.save(new User("Test", "test@example.com"));
        redisTemplate.opsForValue().set("user:1", user);
        
        assertNotNull(redisTemplate.opsForValue().get("user:1"));
    }
}

Способ 9: Проверка constraints и索引

@DataJpaTest
public class ConstraintTest {
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void testUniqueEmailConstraint() {
        User user1 = new User();
        user1.setName("Alice");
        user1.setEmail("duplicate@example.com");
        userRepository.save(user1);
        
        User user2 = new User();
        user2.setName("Bob");
        user2.setEmail("duplicate@example.com");
        
        // Должна выбросить ошибку уникальности
        assertThrows(DataIntegrityViolationException.class, () -> {
            userRepository.save(user2);
            userRepository.flush();  // Принудительная flush для проверки
        });
    }
}

Best Practices тестирования БД

  1. Используй @DataJpaTest для repository тестов

    @DataJpaTest  //快 быстро, загружает только слой данных
    
  2. Используй @SpringBootTest для интеграционных тестов

    @SpringBootTest  // Полный контекст
    
  3. Откатывай изменения с @Transactional

    @Transactional  // Откатит после теста
    
  4. Предпочитай H2 для unit тестов

    • Быстрая в памяти БД
    • Не нужно контейнеров
  5. Используй TestContainers для интеграционных

    • Реальная БД
    • Тестируешь реальные constraints и features
  6. Подготавливай данные в setUp()

    @BeforeEach
    void setUp() {
        // Fixture
    }
    
  7. Тестируй как позитивные, так и негативные кейсы

    // Успешное сохранение
    // Дублирование (constraint)
    // Null значения
    // Граничные случаи
    
  8. Включай SQL логирование в тестах

    logging:
      level:
        org.hibernate.SQL: DEBUG
    

Рекомендуемая стратегия

Unit Tests (H2):
- Repository методы
- Query логика
- Быстрые

Integration Tests (TestContainers):
- Всё вместе
- Миграции
- Constraints
- Реальная БД

E2E Tests (@SpringBootTest):
- Сервисы
- Контроллеры
- Полный путь

В production используй:

  • H2 для unit (быстро)
  • PostgreSQL в TestContainers для интеграции (реально)
  • Отдельное окружение для smoke tests (перед деплоем)

Это даст тебе быстрый feedback в разработке и уверенность в production.

Как протестировать базу данных | PrepBro