Вопрос проверяет понимание подходов к тестированию, которое взаимодействует с базой данных, чтобы обеспечить надежность и изолированность тестов.
Тестирование кода, который взаимодействует с базой данных, — это критически важная практика для обеспечения корректности работы приложения с данными. Оно позволяет убедиться, что запросы выполняются правильно, связи между сущностями работают, а бизнес-логика корректно манипулирует состоянием в БД. Основная сложность заключается в управлении состоянием базы данных между тестами, чтобы каждый тест был изолирован и воспроизводим.
Рассмотрим простой пример интеграционного теста, использующего транзакционную изоляцию через фикстуры pytest.
import pytest
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
# Фикстура для создания engine (здесь используем SQLite в памяти)
@pytest.fixture(scope="session")
def engine():
return create_engine("sqlite:///:memory:")
# Фикстура для создания таблиц перед тестами
@pytest.fixture(scope="session", autouse=True)
def create_tables(engine):
with engine.connect() as conn:
conn.execute(text("""
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
)
"""))
conn.commit()
# Фикстура, предоставляющая сессию для каждого теста с откатом
@pytest.fixture
def db_session(engine):
connection = engine.connect()
transaction = connection.begin()
Session = sessionmaker(bind=connection)
session = Session()
yield session # Сессия передается в тест
session.close()
transaction.rollback() # Откатываем все изменения теста
connection.close()
# Сам тест
def test_create_user(db_session):
# Действие: вставляем запись
db_session.execute(
text("INSERT INTO users (name) VALUES (:name)"),
{"name": "Alice"}
)
db_session.commit()
# Проверка: можем ли мы её прочитать
result = db_session.execute(text("SELECT name FROM users")).fetchone()
assert result[0] == "Alice"
# После теста фикстура db_session выполнит rollback, и база станет пустой.
Вывод/Итог: Тестирование с базой данных необходимо для проверки корректности интеграции приложения с хранилищем данных. Для unit-тестов используйте моки, чтобы изолировать логику. Для интеграционных и end-to-end тестов применяйте транзакции, фикстуры или testcontainers, чтобы обеспечить реалистичное и изолированное окружение. Выбор стратегии зависит от требуемой скорости, точности и сложности проекта.