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

Какие знаешь инструменты для тестирования БД?

2.3 Middle🔥 111 комментариев
#Базы данных и SQL#Тестирование

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

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

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

Инструменты для тестирования БД

Введение

Тестирование базы данных — критическая часть разработки приложений. За 10+ лет я использовал различные инструменты и подходы для обеспечения корректности работы со слоем данных.

1. TestContainers - Контейнеризованные БД

TestContainers — самый популярный инструмент для изоляции тестов БД в контейнерах Docker:

<!-- Maven зависимость -->
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.19.7</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <version>1.19.7</version>
    <scope>test</scope>
</dependency>

Использование в тестах:

import org.testcontainers.containers.PostgreSQLContainer;
import org.junit.jupiter.api.Test;

class UserRepositoryTestContainerTest {
    
    // Один контейнер на все тесты класса
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15-alpine")
        .withDatabaseName("testdb")
        .withUsername("testuser")
        .withPassword("testpass");
    
    static {
        postgres.start();
    }
    
    @Test
    void testUserCreation() {
        // Используем БД в контейнере для тестов
        // Каждый тест имеет чистую БД
    }
}

// Или с Spring Boot тестами
@SpringBootTest
@Testcontainers
class UserServiceIntegrationTest {
    
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
        .withDatabaseName("test")
        .withUsername("test")
        .withPassword("test");
    
    @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 testUserRepositoryIntegration() {
        User user = new User("John", "john@example.com");
        User saved = userRepository.save(user);
        
        assertNotNull(saved.getId());
        assertEquals("John", saved.getName());
    }
}

Преимущества TestContainers:

  • Реальная БД в контейнере, а не mock
  • Изоляция тестов
  • Поддержка множества БД (PostgreSQL, MySQL, MongoDB и т.д.)
  • Параллельное выполнение тестов

2. H2 Database - In-Memory БД

H2 — быстрая in-memory база для интеграционных тестов:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
    <version>2.2.224</version>
</dependency>

Конфигурация в application-test.yml:

spring:
  datasource:
    url: jdbc:h2:mem:testdb;MODE=PostgreSQL
    driver-class-name: org.h2.Driver
    username: sa
    password: 
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    hibernate:
      ddl-auto: create-drop

Использование:

@SpringBootTest
class UserRepositoryH2Test {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private TestEntityManager entityManager;
    
    @Test
    void testUserPersistence() {
        User user = new User("Jane", "jane@example.com");
        User saved = userRepository.save(user);
        
        entityManager.flush();
        entityManager.clear();
        
        User retrieved = userRepository.findById(saved.getId()).orElse(null);
        assertNotNull(retrieved);
        assertEquals("Jane", retrieved.getName());
    }
}

Минусы H2:

  • Не полностью совместима с PostgreSQL синтаксисом
  • Может не обнаружить специфичные для реальной БД ошибки
  • Поведение отличается от production БД

3. DBUnit - Подготовка данных для тестов

DBUnit — инструмент для загрузки и проверки состояния БД:

<dependency>
    <groupId>org.dbunit</groupId>
    <artifactId>dbunit</artifactId>
    <version>2.8.1</version>
    <scope>test</scope>
</dependency>

XML файл с тестовыми данными (dataset.xml):

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <users id="1" name="John" email="john@example.com"/>
    <users id="2" name="Jane" email="jane@example.com"/>
    <posts id="1" user_id="1" title="Post 1" content="Content 1"/>
</dataset>

Использование в тестах:

@RunWith(SpringRunner.class)
@SpringBootTest
@DbUnitConfiguration(databaseConnection = "dbUnitDatabaseConnection")
class UserRepositoryDbUnitTest extends DBTestCase {
    
    @Autowired
    private DataSource dataSource;
    
    @Autowired
    private UserRepository userRepository;
    
    @Override
    protected DatabaseOperation getTearDownOperation() throws Exception {
        return DatabaseOperation.DELETE_ALL;
    }
    
    @Override
    protected IDataSet getDataSet() throws Exception {
        return new XmlDataSet(new FileInputStream("src/test/resources/dataset.xml"));
    }
    
    @Test
    void testUserRepository() {
        List<User> users = userRepository.findAll();
        assertEquals(2, users.size());
        
        User john = userRepository.findByName("John");
        assertEquals("john@example.com", john.getEmail());
    }
}

4. Flyway/Liquibase - Миграция БД в тестах

Flyway — управление миграциями БД для воспроизводимых тестов:

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
    <version>9.22.3</version>
</dependency>

SQL миграция (V1__Initial_schema.sql):

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE posts (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL REFERENCES users(id),
    title VARCHAR(200) NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Использование:

@SpringBootTest
class UserRepositoryFlywayTest {
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void testDatabaseMigrations() {
        // Flyway автоматически применит все миграции перед тестом
        User user = new User("Alice", "alice@example.com");
        User saved = userRepository.save(user);
        
        assertNotNull(saved.getId());
    }
}

5. REST Assured - Тестирование REST API с БД

REST Assured — удобно тестировать API с проверкой БД:

<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>rest-assured</artifactId>
    <version>5.4.0</version>
    <scope>test</scope>
</dependency>

Использование:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserApiE2ETest {
    
    @LocalServerPort
    private int port;
    
    @Autowired
    private UserRepository userRepository;
    
    @BeforeEach
    void setUp() {
        RestAssured.baseURI = "http://localhost";
        RestAssured.port = port;
        userRepository.deleteAll(); // Очищаем БД перед тестом
    }
    
    @Test
    void testCreateUserThroughApi() {
        given()
            .contentType(ContentType.JSON)
            .body(new User("Bob", "bob@example.com"))
        .when()
            .post("/api/users")
        .then()
            .statusCode(201)
            .body("id", notNullValue())
            .body("name", equalTo("Bob"));
        
        // Проверяем что пользователь сохранился в БД
        Optional<User> user = userRepository.findByEmail("bob@example.com");
        assertTrue(user.isPresent());
        assertEquals("Bob", user.get().getName());
    }
}

6. Arquillian - Интеграционное тестирование

Arquillian — для полного интеграционного тестирования с контейнерами:

<dependency>
    <groupId>org.jboss.arquillian.junit</groupId>
    <artifactId>arquillian-junit-container</artifactId>
    <version>1.8.1.Final</version>
    <scope>test</scope>
</dependency>

7. QueryDSL - Типизированные запросы в тестах

QueryDSL — для построения типизированных запросов при проверке данных:

@SpringBootTest
class UserRepositoryQueryDslTest {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private JPAQueryFactory queryFactory;
    
    private QUser qUser = QUser.user;
    
    @Test
    void testComplexQuery() {
        // Создаем тестовые данные
        User user1 = new User("John", "john@example.com");
        User user2 = new User("Jane", "jane@example.com");
        userRepository.saveAll(List.of(user1, user2));
        
        // Проверяем с типизированным запросом
        List<User> results = queryFactory
            .selectFrom(qUser)
            .where(qUser.name.contains("J"))
            .fetch();
        
        assertEquals(2, results.size());
    }
}

8. Mockito + Spy - Мокирование слоя данных

Mockito — для unit тестов без реальной БД:

class UserServiceUnitTest {
    
    @Mock
    private UserRepository userRepository;
    
    @InjectMocks
    private UserService userService;
    
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }
    
    @Test
    void testGetUserById() {
        // Подготавливаем mock
        User user = new User("John", "john@example.com");
        when(userRepository.findById(1L))
            .thenReturn(Optional.of(user));
        
        // Проверяем
        User result = userService.getUserById(1L);
        
        assertEquals("John", result.getName());
        verify(userRepository, times(1)).findById(1L);
    }
}

9. JdbcTestUtils - Утилиты для тестов

Spring JdbcTestUtils — утилиты для очистки и подготовки БД:

@SpringBootTest
class DatabaseCleanupTest {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @BeforeEach
    void cleanDatabase() {
        // Удаляем все данные перед тестом
        JdbcTestUtils.deleteFromTables(jdbcTemplate, "posts", "users");
    }
    
    @Test
    void testWithCleanDatabase() {
        // Гарантированно пустая БД
    }
}

Сравнение инструментов

ИнструментНазначениеСкоростьРеалистичность
TestContainersИнтеграционные тестыСредняяОчень высокая
H2 DatabaseБыстрые интеграционныеОчень высокаяСредняя
DBUnitПодготовка данныхСредняяВысокая
FlywayМиграции БДСредняяВысокая
REST AssuredE2E тесты APIСредняяОчень высокая
MockitoUnit тестыОчень высокаяНизкая
QueryDSLПроверка данныхСредняяВысокая

Best Practices

  1. Используйте TestContainers для интеграционных тестов — максимальная реалистичность
  2. H2 для быстрых unit тестов — скорость критична
  3. Очищайте БД перед каждым тестом — изоляция тестов
  4. Используйте Flyway/Liquibase — воспроизводимые миграции
  5. Не мокируйте репозитории в интеграционных тестах — тестируйте реальное взаимодействие
  6. Мокируйте сервисы в unit тестах — быстрота и изоляция
  7. Документируйте fixture данные — другие разработчики должны понимать setup

Мой подход в проектах

Обычно я комбинирую:

  • Unit тесты — Mockito с H2 для быстрости (< 1 сек)
  • Интеграционные тесты — TestContainers с реальной БД (3-5 сек)
  • E2E тесты — REST Assured с TestContainers (10+ сек)
  • Миграции — Flyway для воспроизводимости

Выводы

Правильное тестирование БД:

  • Обнаруживает баги на раннем этапе
  • Даёт confidence в production коду
  • Документирует expected behavior
  • Улучшает архитектуру (если сложно тестировать, архитектура плоха)
Какие знаешь инструменты для тестирования БД? | PrepBro