История вопроса
Ручная проверка бизнес-логики в SQL (на уровне хранимых процедур, функций, триггеров) ведет к ошибкам, которые всплывают только в продакшне. Долгое время тестирование SQL-сценариев было неформальным и нестандартизированным. Однако развитие CI/CD-технологий требует автоматизированных тестов и для SQL-кода.
Проблема
Большинство разработчиков ограничивается только тестами на уровне приложения. Отсутствие проверки самих SQL-процедур и функций приводит к дефектам, которые не покрываются ни одним тест-пакетом — например, при изменении логики UDF или регрессе в отчетах.
Решение
В рабочих процессах современных команд организуют unit и интеграционные тесты прямо для SQL-кода. Для unit-тестирования используют фреймворки tSQLt (SQL Server), utPLSQL (Oracle), pgTAP (PostgreSQL), а для интеграционного — отдельные среды для поднятия временных баз, применения миграций и проверки бизнес-сценариев.
Пример unit-теста на pgTAP:
-- Проверяем отделение зарплатой SELECT plan(2); SELECT is( (SELECT calc_salary(1)), 1000, 'Salary for user 1 correct' ); SELECT isnt( (SELECT calc_salary(2)), 0, 'User 2 salary is not zero' ); SELECT finish();
Код интеграционного теста для CI/CD:
psql -U user -d testdb < migrations.sql psql -U user -d testdb < test_data.sql psql -U user -d testdb -c "SELECT * FROM my_procedure_test();"
Ключевые особенности:
Можно ли обойтись автотестами только на уровне приложения, если процедуры большие? Нет, потому что тесты UI/API не гарантируют корректность работы именно SQL-логики (например, неверные условия внутри хранимых функций или нарушения при обновлении данных). Unit-тесты должны охватить все рутовые ветвления исполнений в самом SQL-коде.
Достаточно ли "ручных" запусков тест-скриптов — ведь в базе мало изменений? Недостаточно, даже в небольших проектах появляются баги после изменений схемы или логики. Автоматизация тестирования в CI-процессе уменьшает человеческий фактор и предотвращает регрессии.
Можно ли тестировать только "критические" процедуры, остальное пропустить? Лучший подход — по возможности покрывать максимальное число функций, особенно если в будущем код будет менять несколько команд: неочевидные вычисления и edge-cases чаще всего проявляются именно в нестандартных ветках.
При доработке процедуры расчета скидок тестировали руками пару кейсов, основные ветки логики упустили. В продакшне клиенты начали получать неверные скидки, разбираться пришлось несколько дней.
Плюсы:
Экономия времени в начале.
Минусы:
Потери на ручном исправлении, неудобство при доработках и рефакторинге.
Разработали unit-тесты с pgTAP на все ключевые UDF и процедуры, интеграционные тесты прогоняются через CI на каждом мерже ветки. Ошибки и регрессы выявляются до деплоя.
Плюсы:
Стабильность функций, возможность быстро дорабатывать бизнес-логику, минимальные баги в продакшне.
Минусы:
Требуется вложение времени для старта и поддержки тестовой базы.