pytest 如何在所有测试类之前运行一次方法
用 scope="session" 的 fixture 复用会话级初始化;默认首次被依赖时执行,autouse 可让可见测试自动依赖。参数化和 xdist worker 需要额外区分。
一句话回答:定义一个 scope="session" 的 fixture——未参数化时,它在首次被测试依赖时创建,并在当前测试进程会话内复用,不一定在会话启动时执行。把它放在项目根目录的 conftest.py 里可被该目录及子目录下的测试发现,不需要在测试文件 import;想让所有测试自动用它(不写参数)就加 autouse=True。
标准写法
# conftest.py(放在测试根目录)
import pytest
@pytest.fixture(scope="session")
def global_setup():
# 首次有测试依赖该 fixture 时初始化
print("初始化:建测试数据库、起服务……")
yield
# 全部测试结束后执行一次(清理)
print("清理:释放资源")
fixture 里 yield 之前是 setup,之后是 teardown。若 setup 在 yield 前失败,其后清理段不会执行;已经创建的外部资源应使用上下文管理器或适当的 finalizer 保护。表中的执行频率按未参数化、单进程情况描述;参数化可能导致同一作用域内多次创建。
作用域对照表
| scope | 执行频率 | 典型用途 |
|---|---|---|
function(默认) |
每个测试函数一次 | 临时数据、mock |
class |
每个测试类一次 | 类级资源 |
module |
每个 py 文件一次 | 模块级连接 |
package |
每个包一次 | 包级初始化 |
session |
单进程会话内复用 | ⭐ 全局一次性初始化 |
两种使用方式
显式依赖(测试声明需要它):
def test_something(global_setup):
...
自动生效(autouse,测试无感知):
@pytest.fixture(scope="session", autouse=True)
def global_setup():
...
一次性的环境初始化(建库、起容器、写配置)通常用 autouse + session 组合。
配套技巧:每次测试前重置数据
以下是放在 conftest.py 中的 SQLite 简化示例:session 级建资源、function 级清数据。示例不等同于真实服务的事务隔离方案。
import sqlite3
import pytest
@pytest.fixture(scope="session")
def db_connection():
conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
yield conn
conn.close()
@pytest.fixture(autouse=True)
def reset_table(db_connection):
db_connection.execute("DELETE FROM users")
db_connection.commit()
常见问题(FAQ)
Q:fixture 写在测试文件里和 conftest.py 里有区别吗?
A:作用范围不同。测试文件里的 fixture 只对该文件可见;conftest.py 里的对该目录及子目录全部可见。全局性 fixture 放根 conftest.py。
Q:xdist 并行时 session fixture 会跑几次?
A:每个 worker 进程各跑一次(session 是进程级概念)。真正的"全局只跑一次"(如建共享数据库)需要文件锁或 pytest-xdist 的 FileLock 方案协调。
Q:unittest 风格的 setUpClass 和 fixture 怎么选?
A:pytest 项目统一用 fixture——可组合、有 teardown、支持参数化;setUpClass 每个类都跑一次,做不到真正的"全局一次"。混用容易乱,迁移期例外。
核查依据
本文依据官方文档核对,示例未在实际业务环境运行;上线前请按部署版本、权限与数据范围验证。