pytest fixtures + factory_boy with SQLAlchemy 2 sessions: working versions (tested 2026-10-15)
Transactional pytest fixtures that roll back per test, plus factory_boy SQLAlchemy factories with sequences, SubFactory and traits.
passed3 passed · 0 failed2026-10-15 11:56 UTC- Status
- passed
- Last passed
- 2026-10-15 11:56 UTC
- Tests
- 3 / 3
- Language
- Python
- Runtime
- Python 3.13.16
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 38730348195
The code
Exactly the files that passed. Your agent can pull them with get_recipe("pytest-factory-boy-sqlalchemy") (install), or see them on GitHub.
sqlalchemy==2.1.4 factory-boy==3.3.3 pytest==9.1.1
from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
email: Mapped[str] = mapped_column(unique=True)
is_admin: Mapped[bool] = mapped_column(default=False)
class Order(Base):
__tablename__ = "orders"
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
total_cents: Mapped[int]
user: Mapped[User] = relationship()
import factory
from models import Order, User
class UserFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = User
sqlalchemy_session_persistence = "flush" # get ids without committing
email = factory.Sequence(lambda n: f"user{n}@example.com")
is_admin = False
class Params:
admin = factory.Trait(is_admin=True, email=factory.Sequence(lambda n: f"admin{n}@example.com"))
class OrderFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = Order
sqlalchemy_session_persistence = "flush"
user = factory.SubFactory(UserFactory)
total_cents = factory.Faker("pyint", min_value=100, max_value=10000)
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from models import Base
from tests.factories import OrderFactory, UserFactory
@pytest.fixture(scope="session")
def engine():
eng = create_engine("sqlite://")
Base.metadata.create_all(eng)
return eng
@pytest.fixture
def session(engine):
# each test runs inside a transaction that is rolled back afterwards
conn = engine.connect()
tx = conn.begin()
s = Session(bind=conn, join_transaction_mode="create_savepoint")
for f in (UserFactory, OrderFactory):
f._meta.sqlalchemy_session = s
yield s
s.close()
tx.rollback()
conn.close()
from sqlalchemy import func, select
from models import Order, User
from tests.factories import OrderFactory, UserFactory
def test_subfactory_creates_user(session):
o = OrderFactory()
assert o.id and o.user.id and 100 <= o.total_cents <= 10000
def test_trait_and_batch(session):
UserFactory.create_batch(3)
admin = UserFactory(admin=True)
assert admin.is_admin and admin.email.startswith("admin")
assert session.scalar(select(func.count()).select_from(User)) == 4
def test_isolation_between_tests(session):
assert session.scalar(select(func.count()).select_from(Order)) == 0
[pytest] pythonpath = .
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.
Related stacks
Alembic autogenerate compare_metadata drift check against SQLite
Declare SQLAlchemy 2 models with a naming convention, then detect schema drift offline by diffing the live SQLite schema against the metadata with Alembic compare_metadata.
Async repository pattern with SQLAlchemy 2 AsyncSession on aiosqlite
A typed async repository over SQLAlchemy 2 AsyncSession with eager loading, foreign keys enforced on SQLite, and offline pytest-asyncio tests against an in-memory database.
Capture loguru logs in pytest caplog via a propagate sink and structured extra
Forward loguru records into stdlib logging so pytest caplog sees them, keep bound context as structured extra, and test it offline without a real log backend.
FastAPI + SQLAlchemy 2 CRUD with dependency-injected sessions
FastAPI routes backed by SQLAlchemy 2.x typed models, a per-request session dependency, and TestClient tests on SQLite.
All pytest recipesAll factory-boy recipesAll SQLAlchemy recipes