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

«Как вы подходите к тестированию базы данных в проекте?» — вопрос из категории Базы данных и SQL, который задают на 10% собеседований QA Тестировщик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Тестирование БД включает несколько критически важных слоёв:

1. Тестирование схемы (Structure Testing):

-- Проверка целостности схемы
SELECT 
    table_name,
    column_name,
    data_type,
    is_nullable,
    column_default
FROM information_schema.columns 
WHERE table_schema = 'public'
ORDER BY table_name, ordinal_position;

-- Проверка индексов
SELECT 
    tablename,
    indexname,
    indexdef 
FROM pg_indexes 
WHERE schemaname = 'public';

2. Тестирование ограничений (Constraint Testing):

  • NOT NULL – попытка вставки NULL в обязательные поля
  • UNIQUE – проверка на дубликаты
  • FOREIGN KEY – каскадные операции и ссылочная целостность
  • CHECK – валидация бизнес-правил на уровне БД

3. Тестирование миграций:

# Пример теста миграции с Alembic
from alembic import command
from alembic.config import Config

def test_migration_up_down():
    alembic_cfg = Config("alembic.ini")

    # Применяем миграцию
    command.upgrade(alembic_cfg, "head")

    # Проверяем, что таблица создалась
    with engine.connect() as conn:
        result = conn.execute("SELECT EXISTS (SELECT FROM new_table)")
        assert result.scalar() is True

    # Откатываем миграцию
    command.downgrade(alembic_cfg, "-1")

4. Тестирование производительности:

  • EXPLAIN ANALYZE для анализа планов запросов
  • Тестирование на больших объемах данных (100K+ записей)
  • Проверка блокировок и deadlock'ов при конкурентном доступе

5. Тестирование резервного копирования:

  • Восстановление из backup на тестовом окружении
  • Проверка целостности данных после восстановления
  • Тестирование point-in-time recovery

6. Интеграционные тесты:

@pytest.fixture
def test_db():
    # Создание изолированной тестовой БД
    engine = create_engine(TEST_DATABASE_URL)
    Base.metadata.create_all(engine)
    return engine

def test_transaction_rollback(test_db):
    with test_db.begin() as conn:
        conn.execute("INSERT INTO users (name) VALUES ('test')")
        # Явный откат
        raise Exception("Force rollback")

    # Проверяем, что данные не сохранились
    result = test_db.execute("SELECT COUNT(*) FROM users")
    assert result.scalar() == 0