首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >避免 pytest hook 覆盖:快用hookwrapper 守护 session 完整性

避免 pytest hook 覆盖:快用hookwrapper 守护 session 完整性

作者头像
用户12558604
发布2026-06-17 17:24:24
发布2026-06-17 17:24:24
140
举报

在 pytest 测试项目中,我遇到过这样的场景 内外层同时写environment.properties,内层hook的将会被覆盖掉

代码语言:javascript
复制
project/
├── conftest.py            # 外层 hook
└── module/
    └── conftest.py        # 内层 hook

当外层定义了 pytest_sessionfinish 时,它会完全覆盖内层的同名 hook。这会导致:

  1. 1. 内层的某些逻辑被跳过
  2. 2. 测试报告生成中断

解决方案:使用hookwrapper 模式

伪代码

代码语言:javascript
复制
# module/conftest.py
import pytest

@pytest.hookimpl(hookwrapper=True)
def pytest_sessionfinish(session, exitstatus):
    # 执行外层和其他插件的 sessionfinish
    yield
    
    # 追加自定义行为(安全区域)
    # your_code

示例两个conftest都会修改到 environment.properties

代码语言:javascript
复制
# module/conftest.py
import os
import pytest
from pathlib import Path

def merge_properties(base: dict, new: dict) -> dict:
    """智能合并属性字典"""
    merged = base.copy()
    for k, v in new.items():
        # 处理数组型属性(如 tags)
        if k in merged andisinstance(merged[k], list):
            ifisinstance(v, list):
                merged[k] = merged[k] + v
            else:
                merged[k] = merged[k] + [v]
        else:
            merged[k] = v
    return merged

@pytest.hookimpl(hookwrapper=True)
def pytest_sessionfinish(session, exitstatus):
    yield # 等待其他 hook 完成
    
    report_dir = session.config.option.allure_report_dir
    if not report_dir:
        return
    
    prop_file = Path(report_dir) / "environment.properties"
    
    # 读取现有属性
    existing_props = {}
    if prop_file.exists():
        with open(prop_file, "r") as f:
            for line in f:
                if"="in line:
                    key, value = line.strip().split("=", 1)
                    existing_props[key] = value
    
    # 定义要追加/修改的属性
    new_props = {
        "MODULE_LEVEL": "inner_module",
        "EXTRA_TAGS": ["stability", "performance"],
        "CONFTEST_VERSION": os.getenv("CONFTEST_VERSION", "1.0.0")
    }
    
    # 智能合并
    merged_props = merge_properties(existing_props, new_props)
    
    # 重写文件
    withopen(prop_file, "w") as f:
        for key, value in merged_props.items():
            ifisinstance(value, list):
                # 处理数组属性
                for item in value:
                    f.write(f"{key}={item}\n")
            else:
                f.write(f"{key}={value}\n")

执行流程

简单画了个原理图

执行顺序保障

  • • 外层 hook 优先执行
  • • 自定义逻辑最后执行

扩展:

1. 错误隔离

代码语言:javascript
复制
try:
    yield
finally:
    # 即使外层hook崩溃也会执行
    log_cleanup_actions()

2. 上下文访问

代码语言:javascript
复制
@pytest.hookimpl(hookwrapper=True)
def pytest_sessionfinish(session, exitstatus):
    # 获取yield前的结果
    result = yield
    
    # 检查外层hook的执行结果
    if result.excinfo:
        send_alert("外层hook执行失败")

备注:

Hook 类型

适用场景

hookwrapper

包裹其他hook实现

tryfirst/trylast

控制执行顺序

optionalhook

可选实现

historic

跨会话事件

通过本文介绍的技术可以在不破坏现有测试框架的前提下,安全地扩展 pytest 的会话终结行为

参考文档:

https://docs.pytest.org/en/stable/

#Pytest #PytestHook

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-07-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程拾光 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 解决方案:使用hookwrapper 模式
    • 伪代码
    • 示例两个conftest都会修改到 environment.properties
    • 执行流程
    • 扩展:
  • 备注:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档