如何从独立Poetry包中加载pytest fixtures?
我正尝试将部分测试配置迁移至公共Poetry包,避免重复编写相同的fixtures。现有user-service项目,在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的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已配置引入common包:
[tool.poetry] name = "user-service" version = "0.1.0" description = "" authors = ["Misha4231 <misha@gmail.com>"] 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
但每个测试都报错:
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包中加载pytest fixtures?
1. 确保公共包的测试模块被正确打包
Poetry默认不会打包tests目录下的内容,导致common/tests/db_conf.py无法被包含在安装的包中,pytest自然找不到该模块。
修改common项目的pyproject.toml,添加include配置,明确将需要共享的fixture文件打包:
[tool.poetry] # 保留原有配置 include = [ "common/**/*", "common/tests/db_conf.py" # 指定共享的fixture文件 ]
如果需要共享整个测试目录,可改为:
include = [ "common/**/*", "common/tests/**/*" ]
2. 验证pytest插件路径
确认pytest_plugins = ["common.tests.db_conf"]的模块路径正确。由于common是根包,tests/db_conf.py在其目录下,该路径是正确的,但需确保文件已被打包并能被Python导入。
3. 检查Docker环境的依赖安装
你在user-service中用了group.local的依赖配置,需确保Docker环境中安装的是本地开发版本的common包。在Dockerfile中执行poetry install --with local,这样才能加载本地修改后的common包内容。
4. 移除冗余导入
user-service/tests/conftest.py中导入common.tests.db_conf内fixture的代码是多余的,只要通过pytest_plugins声明插件,pytest会自动加载该模块的fixture,可删除这部分代码:
# 删除以下代码 from common.tests.db_conf import ( # noqa: F401 anyio_backend, test_engine, setup_db, db_session, )
5. 测试验证
修改完成后,先在本地运行pytest --fixtures,查看是否能列出db_session等来自common包的fixture,确认加载成功后再在Docker环境中重新构建镜像并运行测试。
内容的提问来源于stack exchange,提问作者Misha

