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注册的清理函数。
-