集成测试中pytest依赖注入的关键是明确注入内容、隔离策略与清理责任:用@fixture声明资源,按module作用域复用外部连接,聚合fixture组合依赖,cleanup需显式事务回滚或清空,并确保返回对象线程安全。

集成测试里用 pytest 做依赖注入,关键不是“能不能注入”,而是“注入什么、怎么隔离、谁负责清理”。直接上结论:用 @pytest.fixture 声明资源(如数据库连接、HTTP 客户端),让测试函数显式接收它们;但必须控制作用域、避免共享可变状态、明确 cleanup 时机。
fixture 该用什么作用域?别默认用 function
集成测试常涉及真实外部资源(PostgreSQL、Redis、HTTP 服务),这些开销大、初始化慢,不能每个测试都重建。但也不能盲目设 scope="session" —— 它会让所有测试共享同一个连接对象,一旦某个测试改了连接状态(比如执行了 conn.autocommit = False),后续测试就可能失败。
实操建议:
- 数据库连接类 fixture 优先用
scope="module":一个测试文件内复用,避免重复建连,又不会跨文件污染 - 如果真要 session 级(比如启动一次 mock server),必须用
yield+try/except保证 cleanup,例如:yield server后跟server.stop(),且在 yield 前加try捕获启动失败 - 绝对不要给
scope="session"的 fixture 依赖scope="function"的 fixture —— pytest 会在收集阶段直接报CircularFixtureRefError
怎么注入多个真实依赖而不写死顺序?
集成测试往往需要 DB + Cache + Config 三者协同,但 user_service(db, cache, config) 这种写法容易漏传、难维护。正确做法是让 fixture 自己组合依赖,而不是在测试函数里手动拼。
常见错误现象:测试函数参数列表越来越长,改一个依赖就得同步改十几个测试签名。
实操建议:
- 定义聚合 fixture,例如
@pytest.fixturedef full_stack_env(db_conn, redis_client, app_config),它内部组装并返回一个命名元组或简单 dict - 测试函数只依赖
full_stack_env,不直接引用底层资源 —— 修改底层依赖时,只需改这个 fixture,测试函数完全不动 - 避免在聚合 fixture 里做业务逻辑判断(比如根据环境变量切换 DB),那会增加不可测分支;配置差异应通过参数化 fixture 或
pytest.skip()控制
为什么测试跑着跑着就连接超时或数据残留?
这不是网络问题,大概率是 fixture 的 cleanup 没生效,或者多个测试并发修改了同一份共享数据。
使用场景:你用 db_conn fixture 插入测试数据,但没清空表;或用了 scope="module",第二个测试读到了第一个测试写的数据。
实操建议:
- 每个测试开始前,用 fixture 初始化干净状态:比如在
db_connfixture 中,yield conn前执行truncate_all_tables(conn) - 如果数据库支持事务,优先用
SAVEPOINT+ rollback,比 truncate 快得多;PostgreSQL 可用conn.cursor().execute("ROLLBACK TO SAVEPOINT test_start") - 对非数据库资源(如文件系统、本地缓存目录),在 fixture 的 cleanup 阶段用
shutil.rmtree或os.unlink,别依赖测试结束后的自动清理
最易被忽略的点:fixture 返回的对象本身是否线程安全。比如你用 requests.Session() 作为 fixture 返回值,在 pytest-xdist 并行运行时,多个测试会共用同一个 session 实例,Cookie、headers 都可能串。解决办法不是禁用并行,而是确保每个测试拿到的是独立实例 —— 就算作用域是 function,也要确认构造逻辑每次真的新建对象,而不是返回全局单例。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











