### ⚙️ 配置阶段 (最常用)
主要用于读取命令行参数、修改配置或注册插件。
| 钩子函数 | 用途说明 | 常用场景 |
| ------ |------ |------ |
| `pytest_addoption(parser)` | 添加自定义命令行选项。 | 定义环境变量(如 `--env=prod`)、开关(如 `--send-report`)。 |
| `pytest_configure(config)` | 配置 pytest 对象。 | 创建日志目录、初始化全局变量、注册自定义插件。 |
| `pytest_sessionstart(session)` | 会话开始时调用。 | 连接数据库、启动服务、生成测试报告头信息。 |
| `pytest_sessionfinish(session, exitstatus)` | 会话结束时调用。 | 断开数据库连接、发送测试报告邮件、清理临时文件。 |
---
### 📂 用例收集阶段
控制测试用例如何被发现和组织。
| 钩子函数 | 用途说明 | 常用场景 |
| ------ |------ |------ |
| `pytest_collection_modifyitems(session, config, items)` | 在用例收集完成后,对其进行修改。 | **修改执行顺序**、**重命名用例**、**添加标记**、跳过特定用例。 |
| `pytest_ignore_collect(collection_path, path, config)` | 决定是否忽略某个路径的收集。 | 根据配置跳过特定目录下的测试。 |
---
### 🏃 测试执行阶段
在测试运行前后插入逻辑,常用于数据驱动或环境准备。
| 钩子函数 | 用途说明 | 常用场景 |
| ------ |------ |------ |
| `pytest_runtest_setup(item)` | 每个测试用例执行前的 setup 阶段。 | 用例级别的前置处理。 |
| `pytest_runtest_call(item)` | 执行测试用例函数本身。 | 可以在这里捕获异常或记录函数执行时间。 |
| `pytest_runtest_teardown(item, nextitem)` | 每个测试用例执行后的 teardown 阶段。 | 用例级别的后置清理。 |
| `pytest_keyboard_interrupt(excinfo)` | 用户按下 Ctrl+C 时调用。 | 捕获中断信号,进行优雅的退出处理。 |
---
### 📊 报告与输出阶段
生成或修改测试结果报告。
| 钩子函数 | 用途说明 | 常用场景 |
| ------ |------ |------ |
| `pytest_runtest_makereport(item, call)` | 创建测试报告对象。 | **最强大的钩子之一**,用于获取用例的执行结果(通过/失败/跳过)。 |
| `pytest_terminal_summary(terminalreporter, exitstatus, config)` | 在控制台输出最终摘要。 | 在终端打印自定义统计信息(如通过率、耗时)。 |
| `pytest_html_report_title(report, data)` | 修改 HTML 报告的标题。 | (配合 `pytest-html` 插件) 自定义报告标题。 |
---
### 💻 实战代码示例
#### 1. 自定义命令行参数与配置
```python
# conftest.py
def pytest_addoption(parser):
parser.addoption(
"--env",
default="test",
choices=["dev", "test", "prod"],
help="Choose the test environment"
)
def pytest_configure(config):
# 将环境变量存入全局配置,方便其他地方取用
env = config.getoption("--env")
config._env = env
print(f"\n🚀 Starting tests in {env} environment...")
```
#### 2. 动态修改用例(重命名、加标记)
```python
# conftest.py
def pytest_collection_modifyitems(config, items):
for item in items:
# 给所有用例名加上环境前缀
item.name = f"{config._env}_{item.name}"
# 自动给没有标记的用例加上 smoke 标记
if not item.get_closest_marker("slow"):
item.add_marker("smoke")
```
#### 3. 捕获测试结果并截图(结合 Selenium/Appium)
```python
# conftest.py
import pytest
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
# 获取钩子执行结果(包含报告对象)
outcome = yield
report = outcome.get_result()
# 只在测试失败时执行
if report.when == "call" and report.failed:
# 假设 fixture 'page' 提供了截图功能
if hasattr(item, 'funcargs'):
page = item.funcargs.get('page')
if page:
# 截图保存逻辑...
print(f"\n❌ Test failed, screenshot taken: {item.name}.png")
```
#### 4. 会话结束发送报告
```python
# conftest.py
def pytest_sessionfinish(session, exitstatus):
print(f"\n📊 Test Summary: {session.testscollected} collected, {session.testsfailed} failed.")
# 这里可以写发送邮件的逻辑
if session.testsfailed == 0:
print("🎉 All tests passed!")
else:
print(f"💣 {session.testsfailed} tests failed. Please check the report.")
```
---
### 💡 核心技巧
- `hookwrapper=True`:这是一个非常重要的参数。当你想在钩子函数执行前后都做点事(比如记录耗时、捕获异常、修改返回值)时,必须加上这个装饰器(如上面的截图例子)。
- **执行顺序**:了解钩子的执行顺序对于调试非常重要。大致顺序是:`addoption` -> `configure` -> `sessionstart` -> `collection_modifyitems` -> `runtest_setup` -> `runtest_call` -> `runtest_teardown` -> `sessionfinish`。
