Какой у вас опыт тестирования хранимых процедур в SQL?

Ответ

Есть опыт тестирования хранимых процедур в PostgreSQL и MySQL. Подход включает проверку:

  1. Функциональной корректности: Соответствие логики бизнес-требованиям.
  2. Обработки граничных случаев и ошибок: Поведение при NULL, невалидных данных, нарушениях ограничений.
  3. Производительности: Время выполнения и нагрузка на БД.
  4. Безопасности: Потенциальные уязвимости, такие как SQL-инъекции.

Пример тестирования процедуры в PostgreSQL:

-- Процедура для добавления пользователя
CREATE OR REPLACE PROCEDURE sp_add_user(
    p_username VARCHAR,
    p_email VARCHAR
)
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO users (username, email) VALUES (p_username, p_email);
END;
$$;

-- Тестовые вызовы и проверки:
-- 1. Успешное добавление
CALL sp_add_user('john_doe', 'john@example.com');
SELECT * FROM users WHERE username = 'john_doe'; -- Должен вернуть запись

-- 2. Проверка на дубликат (если есть UNIQUE constraint)
-- Первый вызов — OK
CALL sp_add_user('test', 'test@mail.com');
-- Второй вызов с теми же данными — ожидается исключение
CALL sp_add_user('test', 'test@mail.com'); -- Должна быть ошибка нарушения уникальности

-- 3. Проверка обработки NULL (в зависимости от требований)
CALL sp_add_user(NULL, 'some@mail.com'); -- Ожидается ошибка или специальная обработка

Тестирование часто проводилось через интеграционные тесты приложения или специализированные инструменты для БД.

Ответ 18+ 🔞

А, вот это тема, про которую я могу рассказывать часами, пока не охрипну! Хранимые процедуры, блядь... Ну, это ж как чёрный ящик в сердце базы данных, который может либо работать, как швейцарские часы, либо нахуй всё развалить, если в нём какой-нибудь пиздец заложен.

Смотри, подход-то в целом правильный, но он сухой, как говно верблюда. Надо с душой подходить, с подозрением, блядь! Ты же не знаешь, какой мудак это писал до тебя. Может, он там накосячил так, что волосы дыбом встанут.

1. Функциональная корректность. Ну, это понятно. Должно делать то, что задумано. Но, ёпта, задумка и реализация — это две большие разницы, как говорят в Одессе. Ты пишешь тест, вызываешь процедуру, смотришь, что в таблицу упало. Если упало не то — пиши пропало, пошёл искать, где логика поехала. Главное — бизнес-требования держать перед глазами, а не тупо код читать. А то бывает, код вроде красивый, а делает какую-то ересь, блядь.

2. Обработка ошибок и граничные случаи. Вот тут-то и начинается самое интересное, ебать мои старые костыли! Это моя любимая часть. Ты должен думать, как самый отъявленный мудак-пользователь. Что он сделает? Подсунет NULL туда, где его быть не должно. Отправит пустую строку. Попробует впихнуть число в текстовое поле. Или, охуенно, — попытается добавить запись, которая уже есть, если на поле стоит UNIQUE. Твоя задача — не просто проверить, что ошибка будет. А КАКАЯ ошибка будет? Она понятная? Или вылезет какая-то дичь на китайском, про какую-нибудь неявную конвертацию типов? Процедура должна падать красиво, с человеческим сообщением, а не просто ERROR: 23505 — и иди, блядь, гугли, что это значит.

3. Производительность. Ах, да, классика! Процедура может работать на тестовых десяти записях быстрее света, а на проде, где их овердохуища, — встать колом и всю систему положить. Надо смотреть, какие запросы внутри. Нет ли там SELECT * без условий по гигантской таблице? Не забыли ли индекс повесить на поле, по которому идёт JOIN? Иногда один просраченный NESTED LOOP вместо HASH JOIN может превратить быструю операцию в адское мучение.

4. Безопасность. Тут вообще отдельная песня. SQL-инъекции — это как дырка в заборе: кажется, мелочь, а потом через неё весь огород вытопчут. Если процедура динамически строит запросы через конкатенацию строк, а не через параметризованные запросы — это красная тряпка. Надо проверять, блядь, нельзя ли через входные параметры что-то подсунуть, что сломает логику или, того хуже, позволит прочитать чужие данные.

Теперь про твой пример. Он хороший, учебный. Но жизнь, сука, сложнее.

-- Вот смотри на эту невинную процедурку. Добавляет юзера.
CREATE OR REPLACE PROCEDURE sp_add_user(
    p_username VARCHAR,
    p_email VARCHAR
)
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO users (username, email) VALUES (p_username, p_email);
END;
$$;

Кажется, проще некуда? Ан нет!

  • Тест 1: Успешное добавление. Вызвали — проверили. Ок.
  • Тест 2: Дубликат. Ты правильно написал. Надо дважды вызвать и ждать ошибки. Но! А если в таблице users нет UNIQUE CONSTRAINT на username или email? Тогда второй вызов пройдёт, и у тебя будет два одинаковых пользователя в системе. Пиздец, да? Значит, сначала надо проверить, а есть ли вообще ограничения. Тест должен это учитывать.
  • Тест 3: NULL. Ты пишешь: "Ожидается ошибка или специальная обработка". Вот именно, "или"! А что, если разработчик был мудак и не поставил NOT NULL на колонки? Тогда NULL спокойно упадёт в базу. А потом в другом месте приложение попробует отправить этому юзеру письмо на NULL-адрес и накроется медным тазом. Поэтому тест должен проверять не просто факт ошибки/не ошибки, а именно то поведение, которое требуется по документации. Если NULL запрещён — должна быть жёсткая ошибка от БД. Если разрешён — надо понять, как система будет с таким "кривым" юзером работать дальше.

И это только вершина айсберга, блядь! А транзакции? А эта процедура вызывается внутри другой? А что, если она триггеры какие-нибудь дергает, которые тоже могут накосячить? В общем, работа тестировщика хранимок — это постоянный поиск подвоха. Надо быть параноиком, который уверен, что код написан идиотами, и его задача — это доказать. Чаще всего, увы, так и есть.