pytest 的架构是围绕一棵节点树 (Node Tree) 构建的。这棵树在收集阶段 (Collection) 被创建,代表了整个测试会话的结构。
1. Config
对象
作用 :这是 pytest 的全局上下文 和控制中心。它贯穿整个测试生命周期的始末,是所有配置信息、插件系统和共享数据的承载者。
关键属性和方法:
-
pluginmanager
: 最重要的属性之一,是pluggy.PluginManager
的实例。所有钩子的调用都通过它进行 (config.hook.x()
实际上是config.pluginmanager.hook.x()
)。 -
option
: 一个argparse.Namespace
对象,存储了所有解析后的命令行参数(如config.option.verbose
,config.option.collectonly
)。 -
_inicache
: 存储从pytest.ini
、pyproject.toml
等配置文件解析出的内容。 -
rootpath
: 项目根目录的路径。 -
stash
: 一个字典类型的属性,用于在插件之间共享自定义数据,非常好用。例如,一个插件可以在config.stash['my_cache'] = {}
中存储数据,另一个插件可以读取它。 -
hook
: 钩子调用器。所有对 pytest 钩子的调用都通过config.hook.hook_name(...)
完成。
生命周期 :在 pytest.main()
开始时创建,在整个会话结束时销毁。
2. Session
对象
作用 :根收集器 (Root Collector),代表一次完整的 pytest 测试会话。它是节点树的根,是所有其他节点(目录、文件、测试项)的父节点。
关键属性和方法:
-
items
: 这是一个极其重要的列表。在收集阶段结束后,它包含了所有将要被执行的测试项 (Item) 的列表。pytest_collection_modifyitems
钩子操作的就是这个列表。 -
config
: 指向全局的Config
对象。 -
collect()
: 方法。开始收集过程,从当前会话(通常是命令行指定的开始路径)向下收集,构建整个节点树。
继承关系 :Session
-> Node
-> Collector
3. Node
基类
作用 :所有其他组件(Session
, Collector
, Item
)的抽象基类。它定义了节点树中每个节点都具有的基本属性和方法。
关键属性和方法:
-
name
: 节点的短名称(如函数名test_my_function
)。 -
nodeid
: 节点的唯一标识符,是它在节点树中的完整路径(如test_file.py::TestClass::test_method
)。这是报告和过滤中使用的关键ID。 -
parent
: 指向父节点的引用(如一个Module
节点的父节点是Session
或Directory
)。 -
config
: 指向全局的Config
对象。 -
ihook
: 一个便捷属性,等价于self.config.hook
,用于在当前节点调用钩子。
4. Collector
收集器
作用 :继承自 Node
。负责收集子节点。它们构成了节点树的枝干。
类型:
-
Session
: 根收集器。 -
Directory
: 代表一个目录,收集其下的__init__.py
文件(如果被视为包)和其他子目录或 Python 文件。 -
File
: 通常具体化为Module
,代表一个 Python 文件(如test_sample.py
)。
关键方法:
-
collect()
: 核心方法 。它被调用时,会返回一个子节点列表。这些子节点可以是更下层的Collector
(如Module
收集Class
),也可以是最终的Item
(如Module
收集Function
)。python
伪代码概念
def collect(self): collected_children = [] # ... 遍历自身范围内的对象(如模块的属性、类的属性)... for obj in found_objects: if is_test_class(obj): collected_children.append(Class.from_parent(parent=self, name=obj.name )) elif is_test_function(obj): collected_children.append(Function.from_parent(parent=self, name=obj.name)) return collected_children
5. Item
测试项
作用 :继承自 Node
。代表一个最小的、可执行的测试单元 ,是节点树的叶子。最常见的两种是 Function
(测试函数)和 UnitTestCase
( unittest 风格的测试方法)。
关键方法:
-
runtest()
: 核心方法 。当执行阶段到来时,调用此方法会真正运行测试 。对于pytest.Function
,它内部会调用pytest_pyfunc_call
钩子,从而执行被@pytest.fixture
装饰的依赖项和测试函数本身。 -
funcargs
: 一个字典,在setup
阶段后填充。键是测试函数参数的名称,值是对应的 fixture 返回值。item.runtest()
最终会执行test_function(**item.funcargs)
。
生命周期 :由 Collector
创建,由 pytest_runtest_protocol
执行。
6. Fixture
夹具系统
作用 :依赖注入系统,是 pytest 的核心功能。用于提供测试所需的数据、状态或环境设置/清理。
核心概念:
-
@pytest.fixture
装饰器 :用于声明一个函数是 fixture。可以指定scope
("function"
,"class"
,"module"
,"session"
),autouse
,params
(参数化)等。 -
request
对象 :一个具有魔力的上下文参数,会自动传递给每个 fixture 函数。它提供了关于"谁请求了这个 fixture"的信息。-
request.node
: 获取请求该 fixture 的Item
或Collector
节点。 -
request.function
: 获取请求该 fixture 的测试函数(如果是函数级)。 -
request.cls
: 获取请求该 fixture 的测试类。 -
request.module
: 获取请求该 fixture 的测试模块。 -
request.config
: 获取全局的Config
对象。 -
request.addfinalizer(finalizer_func)
或yield
: 用于定义 teardown 清理逻辑。
-
-
pytest.fixture
内部工作原理:-
发现 :当收集到一个
Item
时,pytest 会分析其参数列表。 -
匹配:对于每个参数名,去寻找同名的 fixture 函数。
-
排序:根据 fixture 的依赖关系和 scope 对它们进行排序。
-
缓存:为相同 scope 的请求缓存 fixture 实例。
-
执行 (Setup) :在测试运行的
setup
阶段,按顺序执行 fixture 函数,获取返回值并存入item.funcargs
。 -
注入 :在
call
阶段,将funcargs
解包并传入测试函数。 -
清理 (Teardown) :在
teardown
阶段,执行通过yield
或addfinalizer
注册的清理函数。
-