从pytest源码的角度分析pytest工作原理
从 pytest
源代码的角度来分析其工作原理,我们需要关注几个关键的部分,特别是 pytest
的启动过程以及测试的收集与执行。下面是基于 pytest
源代码的一个高层次的概述。
pytest 的启动过程
-
命令行解析:
pytest
的入口点是conftest.py
文件中的pytest.main()
函数。- 在这个函数中,首先通过
pytest.config.get_config()
获取配置。 - 接着使用
pytest.config.parse()
来解析命令行参数。
-
配置加载:
pytest
会在当前目录及其父目录递归地查找配置文件,比如pytest.ini
或pyproject.toml
。- 使用
pytest.config.Config
类来存储配置信息。
-
插件管理:
- 通过
pytest.hookspec
和pytest.pluginmanager
来管理插件。 - 插件可以在各个阶段被注册并调用。
- 通过
测试收集过程
-
收集器初始化:
pytest
使用pytest.collect
模块来处理测试收集。Session.from_parent
方法创建一个新的Session
实例。Collector.from_parent
方法用于构建收集器树。
-
测试文件发现:
pytest
通过Session.perform_collect
方法来遍历目录结构并发现测试模块。File.from_parent
方法用于创建File
实例来代表测试文件。Function.from_parent
方法用于创建Function
实例来代表测试函数。
-
测试项构建:
- 一旦发现了测试文件,就会通过
collect
方法来收集文件中的测试函数。 - 测试函数会被转换成
Item
实例。
- 一旦发现了测试文件,就会通过
测试执行过程
-
测试项准备:
- 在测试开始之前,会调用
Session.perform_setup
方法来进行一些预处理。 - 这个阶段可能包括设置环境变量、初始化数据库连接等。
- 在测试开始之前,会调用
-
测试项执行:
Session.runtestloop
方法控制测试项的实际执行。- 对于每一个
Item
实例,都会调用Session.perform_test
方法来执行测试。
-
测试结果收集:
- 测试执行的结果会被收集并存储在
Item
实例中。 - 可能会触发
pytest_runtest_logreport
hook,该 hook 被用来处理测试报告。
- 测试执行的结果会被收集并存储在
-
异常处理:
- 如果测试过程中发生异常,
pytest
会捕获这些异常并记录下来。 - 异常可以通过
pytest_runtest_makereport
hook 来处理。
- 如果测试过程中发生异常,
测试报告生成
Session
实例负责收集所有的测试结果。Session.exitstatus
属性会根据测试结果来确定程序的退出状态码。pytest
可以生成多种格式的报告,这取决于安装的插件。
示例代码片段
下面是一些示例代码片段,展示了 pytest
源代码中的关键部分:
# pytest/conftest.py
def main(args=None):
# 解析命令行参数
config = get_config(args)
# 加载插件
pm = PluginManager()
pm.load_setuptools_entrypoints('pytest11')
# 创建 Session 实例
session = Session.from_parent(config, plugins=pm)
# 执行测试
session.runtestloop()
# 返回退出状态
return session.exitstatus
# pytest/collect.py
def perform_collect(session, collector):
# 收集测试文件和测试函数
items = []
for item in collector.collect():
items.append(item)
return items
# pytest/runner.py
def runtest_protocol(item, nextitem):
# 执行测试项
report = item.runtest()
if report is None:
# 处理异常情况
report = item.makereport()
# 处理测试报告
item.session._hookmanager.hook.pytest_runtest_logreport(report=report)