从独立的Poetry包加载pytest fixtures

后端开发 2026-07-09

我想把一些测试配置移到一个公共的Poetry包中,以避免重复使用相同的fixtures。

在一个项目中,有一个名为 user-service 的包,其中包含 tests 文件夹。就在该文件夹里,我在 user-service/tests/conftest.py 中尝试加载fixtures:

import pytest

from httpx import AsyncClient, ASGITransport
from sqlalchemy.ext.asyncio import AsyncSession

from user_service.main import app
from common.core.database import get_db
from common.tests.db_conf import (  # noqa: F401
    anyio_backend,
    test_engine,
    setup_db,
    db_session,
)

pytest_plugins = ["common.tests.db_conf"]

# Setup http client
@pytest.fixture
async def client(db_session: AsyncSession):
    async def override_get_db():
        yield db_session

    app.dependency_overrides[get_db] = override_get_db

    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://users_test"
    ) as ac:
        yield ac

    app.dependency_overrides.clear()

如你所见,我正在尝试从 common 项目加载fixtures。 common/common/tests/db_conf.py:

import os

import pytest
from sqlalchemy.ext.asyncio import (
    AsyncSession,
    create_async_engine,
    async_sessionmaker,
    AsyncEngine,
)
from sqlalchemy import NullPool
from sqlalchemy.engine import URL

from common.models.base import Model

pytest_plugins = ["anyio"]


@pytest.fixture(scope="session")
def anyio_backend():
    return "asyncio"


# Setup test database engine
@pytest.fixture(scope="session")
def test_engine():
    DATABASE_URL = URL.create(
        drivername="postgresql+psycopg",
        username=os.getenv("POSTGRES_USER"),
        password=os.getenv("POSTGRES_PASSWORD"),
        host=os.getenv("POSTGRES_HOST"),
        port=os.getenv("POSTGRES_PORT"),
        database=os.getenv("POSTGRES_DB"),
    )
    engine = create_async_engine(DATABASE_URL, poolclass=NullPool)
    return engine


# Setup test database
@pytest.fixture(scope="session")
async def setup_db(test_engine: AsyncEngine):
    async with test_engine.begin() as conn:
        await conn.run_sync(Model.metadata.create_all)

    yield

    async with test_engine.begin() as conn:
        await conn.run_sync(Model.metadata.drop_all)

    await test_engine.dispose()


# Setup test database session
@pytest.fixture
async def db_session(test_engine: AsyncEngine, setup_db):
    conn = await test_engine.connect()
    transaction = await conn.begin()

    test_async_session = async_sessionmaker(
        bind=conn,
        class_=AsyncSession,
        expire_on_commit=False,
        join_transaction_mode="create_savepoint",  # data is not really saved in database so that tests are isolated
    )

    async with test_async_session() as session:
        try:
            yield session
        finally:
            await session.close()
            await transaction.rollback()
            await conn.close()

下面是我的 user-service/pyproject.toml,它加载公共包:

[tool.poetry]
name = "user-service"
version = "0.1.0"
description = ""
authors = ["Misha4231 <[email protected]>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.12"
fastapi = "^0.136.1"
ruff = "^0.15.12"
sqlalchemy = "^2.0.49"
pydantic-settings = "^2.14.0"
uvicorn = "^0.46.0"
alembic = "^1.18.4"
psycopg2-binary = "^2.9.12"
asyncpg = "^0.31.0"
structlog = "^25.5.0"
pytest = "^9.0.3"
pytest-asyncio = "^1.3.0"
pytest-mock = "^3.15.1"
httpx = "^0.28.1"
psycopg = "^3.3.3"
anyio = "^4.13.0"
common = "^0.1.1"

[tool.poetry.group.local.dependencies]
common = { path = "../common", develop = true }

[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
pythonpath = ["..", "."]

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

当我用Docker Compose运行测试时:

docker-compose -f docker-compose-test.yml run --rm users_test

每个测试都会收到一个错误,显示 db_session 未找到。

E       fixture 'db_session' not found
>       available fixtures: _class_scoped_runner, _function_scoped_runner, _module_scoped_runner, _package_scoped_runner, _session_scoped_runner, anyio_backend, anyio_backend_name, anyio_backend_options, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, capteesys, class_mocker, client, doctest_namespace, event_loop_policy, free_tcp_port, free_tcp_port_factory, free_udp_port, free_udp_port_factory, mocker, module_mocker, monkeypatch, package_mocker, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, session_mocker, subtests, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, unused_tcp_port, unused_tcp_port_factory, unused_udp_port, unused_udp_port_factory
>       use 'pytest --fixtures [testpath]' for help on them.

如何从另一个Poetry包加载fixtures?

解决方案

问题与pytest插件无关。我在使用Docker Compose运行测试时,忘了在卷中添加包文件夹。每次运行时,代码都不是最新的,因此我以为没有什么能起作用的。

完整项目托管在公开的GitHub仓库中:https://github.com/Misha4231/two-phase-commit-fastapi/tree/main

那一行就足以从独立的Poetry包中加载fixtures:

pytest_plugins = ["common.tests.db_conf"]
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章