← Назад к вопросам
Как в Unit-тестах протестировать синтаксис базы данных
2.0 Middle🔥 201 комментариев
#Тестирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Как в Unit-тестах протестировать синтаксис базы данных
Тестирование синтаксиса SQL в Unit-тестах — важная часть разработки, которая помогает выловить ошибки на ранних этапах. Рассмотрим несколько подходов.
1. Встроенная База Данных (H2, Derby)
Это классический подход для Unit-тестов. Используются in-memory базы, которые быстро запускаются и не требуют конфигурации:
public class UserRepositoryTest {
@BeforeEach
void setUp() {
// H2 автоматически создаёт in-memory БД
Connection conn = DriverManager.getConnection(
"jdbc:h2:mem:testdb;MODE=PostgreSQL"
);
}
@Test
void testInsertUserQuery() throws SQLException {
String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, 1);
stmt.setString(2, "John");
stmt.setString(3, "john@example.com");
int rows = stmt.executeUpdate();
assertEquals(1, rows);
}
}
}
2. Testcontainers для Реальной БД
Если нужна именно та БД, которую используешь в продакшене (PostgreSQL, MySQL):
public class UserRepositoryPostgresTest {
@Container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:15")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@Test
void testComplexQuery() throws SQLException {
String sql = "SELECT u.id, u.name, COUNT(o.id) as order_count " +
"FROM users u LEFT JOIN orders o ON u.id = o.user_id " +
"GROUP BY u.id, u.name HAVING COUNT(o.id) > ?";
try (Connection conn = postgres.createConnection("");
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, 0);
ResultSet rs = stmt.executeQuery();
assertTrue(rs.next()); // проверяем, что запрос выполнился
}
}
}
3. SQL Парсеры и Линтеры
Для проверки синтаксиса SQL можно использовать специализированные библиотеки:
public class SqlSyntaxTest {
@Test
void testSqlSyntaxWithJSQLParser() {
String sql = "SELECT * FROM users WHERE id = 1";
try {
Statement statement = CCJSqlParserUtil.parse(sql);
assertNotNull(statement);
} catch (JSQLParserException e) {
fail("SQL синтаксис неверный: " + e.getMessage());
}
}
}
Добавь зависимость:
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>4.6</version>
<scope>test</scope>
</dependency>
4. JUnit с Spring Boot Test
Для Spring приложений есть встроенная поддержка:
@SpringBootTest
@DataJpaTest // только слой данных
class UserRepositorySpringTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository userRepository;
@Test
void testCustomQuery() {
User user = new User("John", "john@example.com");
entityManager.persist(user);
entityManager.flush();
// Используется твой UserRepository с @Query
List<User> result = userRepository.findByNameStartingWith("J");
assertEquals(1, result.size());
}
}
5. Создание Схемы БД в Тесте
public class DatabaseSchemaTest {
private Connection conn;
@BeforeEach
void setUp() throws SQLException {
conn = DriverManager.getConnection("jdbc:h2:mem:testdb");
// Создаём таблицу
String createTable = "CREATE TABLE users (" +
"id INT PRIMARY KEY, " +
"name VARCHAR(100) NOT NULL, " +
"email VARCHAR(100) UNIQUE, " +
"created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" +
")";
try (Statement stmt = conn.createStatement()) {
stmt.execute(createTable);
}
}
@Test
void testInsertWithUniqueConstraint() throws SQLException {
String insert = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)";
try (PreparedStatement stmt = conn.prepareStatement(insert)) {
stmt.setInt(1, 1);
stmt.setString(2, "John");
stmt.setString(3, "john@example.com");
stmt.executeUpdate();
// Проверяем, что дублирование не сработает
stmt.setInt(1, 2);
stmt.setString(2, "Jane");
stmt.setString(3, "john@example.com");
assertThrows(SQLException.class, stmt::executeUpdate);
}
}
}
6. Проверка Производительности Запроса
@Test
@Tag("performance")
void testQueryPerformance() throws SQLException {
// Вставляем много данных
String insert = "INSERT INTO users (id, name) VALUES (?, ?)";
long startTime = System.currentTimeMillis();
try (PreparedStatement stmt = conn.prepareStatement(insert)) {
for (int i = 0; i < 10000; i++) {
stmt.setInt(1, i);
stmt.setString(2, "User" + i);
stmt.addBatch();
}
stmt.executeBatch();
}
long duration = System.currentTimeMillis() - startTime;
assertTrue(duration < 5000, "Вставка 10k записей должна быть < 5s");
}
7. Тестирование Миграций
public class FlywayMigrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");
@Test
void testAllMigrationsApply() {
Flyway flyway = Flyway.configure()
.dataSource(postgres.getJdbcUrl(),
postgres.getUsername(),
postgres.getPassword())
.load();
MigrateResult result = flyway.migrate();
assertEquals(5, result.migrationsExecuted); // проверяем количество миграций
}
}
Лучшие Практики
- Используй In-Memory БД для скорости — H2 быстрее Testcontainers
- Используй Testcontainers для реальной БД — если нужна точная эмуляция
- Парсируй SQL перед выполнением — ловишь ошибки рано
- Тестируй ограничения (constraints) — UNIQUE, NOT NULL, FK
- Изолируй тесты БД — каждый тест должен быть независим
- Используй транзакции для откатов — чтобы не засорять БД
- Проверяй и граничные случаи — NULL, пустые строки, очень большие значения
Комбинированный подход: быстрые Unit-тесты с H2 + интеграционные тесты с реальной БД в CI.