【pytest 】 pytest 生命周期

【pytest】编写自动化测试用例命名规范README
【python】连接Jira获取token以及jira对象
【python】解析自动化脚本文件并按照=测试周期=存储记录
【python】向Jira推送自动化用例执行成功
【python】向Jira测试计划下,附件中增加html测试报告
【pytest】获取所有用例名称并存于数据库
【Pytest】生成html报告中,中文乱码问题解决方案
【Pytest】将用例运行结果存储到数据库,以sqllite为例

文章目录

  • [pytest 生命周期](#pytest 生命周期)
      • [1. Fixture 的作用域与生命周期](#1. Fixture 的作用域与生命周期)
      • [2. 钩子函数与生命周期](#2. 钩子函数与生命周期)
      • [3. Fixture 作用域层次图](#3. Fixture 作用域层次图)
      • [4. 钩子函数执行顺序图](#4. 钩子函数执行顺序图)
      • [5. 完整生命周期流程(总结)](#5. 完整生命周期流程(总结))
      • 总结

pytest 生命周期

pytest 的生命周期主要由以下几个层次构成,范围从大到小:

  1. 测试会话(Session)生命周期 :一次 pytest 命令的整个过程。
  2. 包/目录(Package)生命周期 :针对每个测试目录(包含 __init__.py 的目录)。
  3. 模块(Module)生命周期 :针对每个 .py 测试文件。
  4. 类(Class)生命周期:针对每个测试类(如果使用了测试类)。
  5. 函数/方法(Function/Method)生命周期 :针对每个以 test_ 开头的函数或方法。

这些生命周期的管理主要通过 Fixturescope 参数和 钩子函数(Hooks) 来实现。


1. Fixture 的作用域与生命周期

Fixture 是理解 pytest 生命周期的关键。它的 scope 参数直接决定了它何时被创建和销毁。

作用域 描述 创建时机 销毁时机
session 整个测试会话 会话开始时创建一次 会话结束时销毁一次
package 一个Python包(目录) 包内第一个测试执行前 包内最后一个测试执行后
module 一个Python模块(文件) 模块内第一个测试执行前 模块内最后一个测试执行后
class 一个测试类 类内第一个测试执行前 类内最后一个测试执行后
function 一个测试函数(默认) 每个测试函数执行前 每个测试函数执行后

示例:

python 复制代码
# conftest.py文件
import pytest

@pytest.fixture(scope="session")
def database_connection():
    # 在整个测试会话中只执行一次
    print("\n=== 建立数据库连接(session)===")
    conn = "模拟的连接对象"
    yield conn
    print("=== 关闭数据库连接(session)===")

@pytest.fixture(scope="module")
def setup_module_data():
    # 在每个测试模块中只执行一次
    print("\n--- 初始化模块数据(module)---")
    data = {"module": "data"}
    yield data
    print("--- 清理模块数据(module)---")

@pytest.fixture(scope="function")
def reset_state():
    # 在每个测试函数中都会执行
    print("> 重置状态(function)")
    yield
    print("> 清理状态(function)")
python 复制代码
# test_demo.py 文件
def test_one(database_connection, setup_module_data, reset_state):
    assert database_connection is not None
    assert "module" in setup_module_data
    print("执行 test_one")

def test_two(database_connection, setup_module_data, reset_state):
    assert database_connection is not None
    assert "module" in setup_module_data
    print("执行 test_two")

运行上述测试的输出可能如下:

text 复制代码
=== 建立数据库连接(session)===

--- 初始化模块数据(module)---
> 重置状态(function)
执行 test_one
.> 清理状态(function)
> 重置状态(function)
执行 test_two
.> 清理状态(function)
--- 清理模块数据(module)---

=== 关闭数据库连接(session)===

从输出可以清晰地看到不同作用域的 fixture 是如何在生命周期中被调用的。


2. 钩子函数与生命周期

钩子函数允许你在 pytest 生命周期的特定时刻插入自己的代码。它们通常定义在 conftest.py 文件或自定义插件中。

以下是一些关键的、与生命周期相关的钩子函数:

会话级别钩子
  • pytest_sessionstart(session): 在测试会话开始之前执行。
  • pytest_sessionfinish(session, exitstatus): 在测试会话结束之后执行,即使发生异常也会执行。
收集阶段钩子
  • pytest_collection_modifyitems(config, items): 在测试用例收集完毕之后被调用,可以用来对测试用例进行排序、过滤等操作。
  • pytest_collection_finish(session): 测试收集完全结束时调用。
运行阶段钩子
  • pytest_runtest_protocol(item, nextitem): 控制每个测试项的运行流程。
  • pytest_runtest_setup(item): 在每个测试项执行之前运行(在 fixture 之前)。
  • pytest_runtest_call(item): 调用测试项(执行测试函数本身)。
  • pytest_runtest_teardown(item, nextitem): 在每个测试项执行之后运行(在 fixture 之后)。
  • pytest_fixture_setup(fixturedef, request): 在执行一个 fixture 的设置代码时调用。
  • pytest_fixture_post_finalizer(fixturedef, request): 在 fixture 的清理代码执行后调用。

示例:使用钩子函数

python 复制代码
# conftest.py文件
def pytest_sessionstart(session):
    print("🔥 pytest 会话开始!")

def pytest_sessionfinish(session, exitstatus):
    print(f"✅ pytest 会话结束!退出状态码:{exitstatus}")

def pytest_collection_modifyitems(config, items):
    print(f"📋 收集到了 {len(items)} 个测试用例")
    # 例如:反转测试顺序(不推荐在生产中使用)
    # items.reverse()

def pytest_runtest_setup(item):
    print(f"🛠️  准备设置:{item.name}")

def pytest_runtest_teardown(item, nextitem):
    print(f"🧹 执行清理:{item.name}")

是 否 是 否 是 否 是 否 是 否 是 否 还有测试项 无测试项 测试会话开始 session级别fixture setup 测试用例收集 对每个测试项循环 是否新包? package级别fixture setup 是否新模块? module级别fixture setup 是否新类? class级别fixture setup function级别fixture setup 执行测试函数 function级别fixture teardown 是否类中最后一个测试? class级别fixture teardown 是否模块中最后一个测试? module级别fixture teardown 是否包中最后一个测试? package级别fixture teardown 下一个测试项 session级别fixture teardown 测试会话结束

3. Fixture 作用域层次图

function class module package session

4. 钩子函数执行顺序图

Session Collection Runtime Fixture 会话开始 pytest_sessionstart 测试收集 pytest_collection_modifyitems 测试执行 pytest_runtest_setup fixture setup (根据作用域) fixture资源 pytest_runtest_call fixture teardown 清理完成 pytest_runtest_teardown loop [每个测试项] 会话结束 pytest_sessionfinish Session Collection Runtime Fixture


5. 完整生命周期流程(总结)

一次典型的 pytest 命令执行会经历以下阶段:

  1. 会话开始
    • 调用 pytest_sessionstart 钩子。
    • 创建 scope="session" 的 fixture。
  2. 测试收集
    • 递归遍历指定目录,寻找 test_*.py 文件和 *_test.py 文件。
    • 在这些文件中,收集 Test 开头的类(不含__init__方法)和 test_ 开头的函数/方法。
    • 调用 pytest_collection_modifyitems 等收集钩子。
  3. 测试执行(对每个测试项循环)
    a. Package/Module 级别 Setup :
    * 如果进入一个新的包/模块,创建 scope="package"/"module" 的 fixture。
    b. Class 级别 Setup :
    * 如果测试项在一个类中,且是类中的第一个测试,创建 scope="class" 的 fixture。
    c. Test Setup :
    * 调用 pytest_runtest_setup 钩子。
    * 创建 scope="function" 的 fixture。
    d. Test Execution :
    * 调用 pytest_runtest_call 钩子,执行测试函数本身的代码。
    e. Test Teardown :
    * 销毁 scope="function" 的 fixture(执行 yield 之后的代码或 addfinalizer 注册的函数)。
    * 调用 pytest_runtest_teardown 钩子。
    f. Class/Package/Module 级别 Teardown :
    * 当一个类/模块/包内的所有测试都执行完毕后,销毁对应的 scope="class"/"module"/"package" 的 fixture。
  4. 会话结束
    • 销毁所有 scope="session" 的 fixture。
    • 调用 pytest_sessionfinish 钩子。
    • 生成测试报告。

总结

  • Fixture 作用域 是控制资源创建和销毁粒度的主要手段。
  • 钩子函数 让你能在生命周期的精确时刻介入,实现自定义行为。
  • 理解生命周期有助于你:
    • 优化测试速度 :将昂贵的操作(如启动数据库)设置为 sessionmodule 级别。
    • 确保测试隔离 :正确使用 function 级别的 fixture 来保证测试不相互影响。
    • 实现复杂的 setup/teardown 逻辑,如全局初始化和清理。

通过结合使用 Fixture 和钩子函数,你可以非常灵活地掌控 pytest 的整个测试流程。

相关推荐
专职1 天前
pytest详细教程
开发语言·python·pytest
乄捷径1 天前
pytest入门到熟练
pytest
专职1 天前
pytest+requests+allure生成接口自动化测试报告
开发语言·python·pytest
小熊出擊2 天前
【pytest】fixture 内省(Introspection)测试上下文
python·单元测试·pytest
小熊出擊3 天前
【pytest】finalizer 执行顺序:FILO 原则
python·测试工具·单元测试·pytest
小熊出擊4 天前
【pytest】使用 marker 向 fixture 传递数据
python·pytest
wa的一声哭了4 天前
Deep Learning Optimizer | Adam、AdamW
人工智能·深度学习·神经网络·机器学习·自然语言处理·transformer·pytest
专职6 天前
pytest生成测试用例,allure生成测试报告
测试用例·pytest
小丁爱养花9 天前
接口自动化测试 - pytest [1]
python·自动化·pytest