Fixtures, временная база и dependency override
Уроки курсаFixtures, временная база и dependency override
Отдельный SQLite-engine и отсутствие SQL при импорте
Чтобы тест работал с отдельной базой, нужно решить одну проблему заранее: в типичном проекте create_all вызывается при импорте модуля, и pytest, собирая тесты, уже создаёт рабочий файл базы. Нужно убрать этот побочный эффект.
Подготовка папки
Создайте папку testing_demo и скопируйте в неё все Python-файлы сохраняющего API из урока 26: models.py, database.py, schemas.py, booking_schemas.py, booking_utils.py, main.py. Файлы *.db не копируйте. Затем замените database.py полностью — файл ниже отличается от оригинала в двух ключевых местах.
# database.py
from sqlalchemy import create_engine, event
from sqlalchemy.orm import sessionmaker
from models import Base
DATABASE_URL = "sqlite:///./roomly_intervals.db"
def create_db_engine(url):
configured_engine = create_engine(url, connect_args={"check_same_thread": False})
@event.listens_for(configured_engine, "connect")
def enable_foreign_keys(connection, connection_record):
cursor = connection.cursor()
cursor.execute("PRAGMA foreign_keys=ON")
cursor.close()
return configured_engine
engine = create_db_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def init_db():
Base.metadata.create_all(bind=engine)
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
Что изменилось и почему
Первое изменение: create_all убран из верхнего уровня модуля и перенесён в функцию init_db. create_engine создаёт ленивый объект — он не открывает файл и не выполняет SQL. Таблицы появятся только при явном вызове init_db. Когда pytest импортирует main, файл roomly_intervals.db не создаётся.
Второе изменение: логика создания engine вынесена в функцию create_db_engine. Это нужно fixture: ей потребуется свой engine с тем же PRAGMA-слушателем, но для другого файла. SQLAlchemy не переносит слушателей событий автоматически — каждый engine настраивается отдельно. Вызов create_db_engine с другим URL даст полноценный тестовый engine с включёнными внешними ключами.
get_db остался прежним: зависимость открывает Session из SessionLocal, выдаёт её обработчику и закрывает в finally. Именно эту функцию fixture заменит в тестах.
Запуск вручную
Для обычного ручного запуска копии сначала создайте схему:
python -c "from database import init_db; init_db()"
python -m uvicorn main:app --reload
Тесты эту команду не используют — они создают схему сами в отдельном файле.
create_all создаёт таблицы, которых ещё нет в указанной базе. Он не добавляет столбцы в уже существующие таблицы и не трогает другие базы.
