0

0

如何解决 pytest 在 Jenkins 中跳过测试但本地正常执行的问题

碧海醫心

碧海醫心

发布时间:2025-12-31 15:30:36

|

383人浏览过

|

来源于php中文网

原创

如何解决 pytest 在 Jenkins 中跳过测试但本地正常执行的问题

pytest 在 jenkins 环境中跳过参数化测试,根本原因在于测试收集阶段(collection phase)早于工作区资源就绪,而 jenkins 清理工作区导致 `@pytest.mark.parametrize` 中调用的 `get_asset()` 提前返回空列表;需将动态资产发现逻辑移至 `pytest_sessionstart` 等会话级钩子中。

在使用 pytest 进行参数化测试时,若测试函数依赖运行时动态生成的参数(如从文件系统读取的测试资产),极易在 CI 环境(尤其是 Jenkins)中出现「本地能跑、Jenkins 跳过」的诡异现象。其本质并非 Jenkins 本身限制,而是 pytest 的测试收集机制CI 工作流时序发生冲突所致。

? 问题根源解析

pytest 在执行任何测试前,会先进入 collection 阶段:静态扫描所有测试模块,解析 @pytest.mark.parametrize、@pytest.fixture 等装饰器,并立即求值其中的参数表达式(如 get_asset())。此时:

  • 若 get_asset() 依赖磁盘上的 asset/ 目录,则该目录必须在 collection 阶段已存在且可访问
  • Jenkins 默认启用 “Delete workspace before build starts”,导致每次构建开始时工作区为空;
  • 因此 collection 阶段调用 get_asset() 返回空列表 → pytest 认为无参数可迭代 → 整个 test_app_launch_asset 被静默跳过(显示为 skipped 或甚至不显示);
  • 而手动登录 Jenkins 机器后执行命令时,工作区已被前次构建残留的 asset/ 填充,故 get_asset() 正常返回 → 测试正常执行。
⚠️ 注意:这不是 get_asset 函数逻辑错误,而是执行时机错配——它被当作“编译期常量”求值,实则应是“运行期动态数据”。

✅ 正确解决方案:使用 pytest 会话级钩子预加载参数

应避免在 @parametrize 中直接调用 IO 密集型函数。推荐将资产发现逻辑提前至 pytest_sessionstart(在 collection 之前执行),并将结果缓存到 config 对象中供后续使用:

AI Note
AI Note

AI Note 助手,像贴心女仆一样助力你的笔记!智能总结内容,精确划重点,提供专业建议,让学习与工作更高效。让你的笔记更清晰、有条理,知识尽在眼前!

下载
# conftest.py
import pytest
from pathlib import Path

def get_asset() -> list[Path]:
    """安全版资产发现:确保路径存在且可读"""
    asset_dir = Path(__file__).parent / 'asset'
    if not asset_dir.exists():
        return []
    return [
        p for p in asset_dir.iterdir()
        if p.is_file() and 'need_to_skip_asset' not in p.name
    ]

def pytest_sessionstart(session):
    """在测试收集前执行:预加载资产列表并挂载到配置"""
    assets = get_asset()
    session.config._metadata['available_assets'] = assets  # 可选:用于报告
    # 将资产列表注入全局变量或 session 属性(推荐)
    session.assets = assets

# 在测试文件中改写参数化逻辑
@pytest.fixture(scope='session', autouse=True)
def available_assets(request):
    """提供会话级 fixture,确保资产列表在测试中可用"""
    return request.session.assets

@pytest.mark.parametrize('asset', [], indirect=True)  # 占位符,实际由 fixture 提供
def test_app_launch_asset(app_binary, asset, available_assets):
    """实际测试逻辑 —— 参数由 fixture 动态注入"""
    print(f'Application: {app_binary}')
    print(f'Asset: {asset}')

    applib.execute(
        cmd=[str(app_binary), str(asset)],
        timeout=15,
    )

但更简洁、符合 pytest 惯例的方式是:完全弃用 @parametrize 的函数调用形式,改用 indirect + fixture 组合

# test_app.py
import pytest

@pytest.fixture(params=[])  # 空占位,真实参数由 conftest.py 注入
def asset(request):
    # 此处可访问 session.assets(需在 conftest.py 中设置)
    session = request.session
    if not hasattr(session, 'assets'):
        pytest.skip("No assets found — check asset directory existence")
    return session.assets[request.param]

# 重写 parametrize:传入索引而非对象
@pytest.mark.parametrize('asset', list(range(100)), indirect=True)
def test_app_launch_asset(app_binary, asset):
    print(f'Application: {app_binary}')
    print(f'Asset: {asset}')
    applib.execute(cmd=[str(app_binary), str(asset)], timeout=15)

不过最推荐的工业级实践是:在 conftest.py 中定义一个 session-scoped fixture,返回完整资产列表,再在测试中通过 for 循环显式遍历(牺牲少量 pytest 原生参数化语法糖,换取完全可控性):

# conftest.py
import pytest
from pathlib import Path

@pytest.fixture(scope='session')
def all_assets():
    asset_dir = Path(__file__).parent / 'asset'
    if not asset_dir.exists():
        pytest.skip(f"Asset directory missing: {asset_dir}")
    return [
        p for p in asset_dir.iterdir()
        if p.is_file() and 'need_to_skip_asset' not in p.name
    ]

# test_app.py
def test_app_launch_asset(app_binary, all_assets):
    """单测试函数内遍历所有资产 —— 完全规避 collection 时序问题"""
    for asset in all_assets:
        print(f'Running on asset: {asset}')
        applib.execute(cmd=[str(app_binary), str(asset)], timeout=15)

? 关键注意事项

  • 永远不要在 @pytest.mark.parametrize(...) 的参数表达式中执行 IO 操作(如读文件、查数据库、调用外部命令);
  • ✅ pytest_sessionstart 和 pytest_configure 是仅有的两个在 collection 之前触发的钩子,适合做预热准备;
  • ✅ Jenkins 构建日志中若看到 collected 0 items,基本可断定 collection 阶段参数源为空;
  • ✅ 在 conftest.py 中添加 print() 或日志输出,验证钩子是否被触发(注意 Jenkins 控制台编码与缓冲);
  • ✅ 本地调试时,可临时在 get_asset() 开头加入 assert Path('asset').exists(),快速暴露环境差异。

通过将动态数据获取逻辑与 pytest 的生命周期对齐,即可彻底解决 Jenkins 下测试“神秘跳过”的问题,让 CI 行为与本地开发保持一致、可预测、可调试。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
python中print函数的用法
python中print函数的用法

python中print函数的语法是“print(value1, value2, ..., sep=' ', end=' ', file=sys.stdout, flush=False)”。本专题为大家提供print相关的文章、下载、课程内容,供大家免费下载体验。

192

2023.09.27

python print用法与作用
python print用法与作用

本专题整合了python print的用法、作用、函数功能相关内容,阅读专题下面的文章了解更多详细教程。

18

2026.02.03

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1565

2023.10.24

session失效的原因
session失效的原因

session失效的原因有会话超时、会话数量限制、会话完整性检查、服务器重启、浏览器或设备问题等等。详细介绍:1、会话超时:服务器为Session设置了一个默认的超时时间,当用户在一段时间内没有与服务器交互时,Session将自动失效;2、会话数量限制:服务器为每个用户的Session数量设置了一个限制,当用户创建的Session数量超过这个限制时,最新的会覆盖最早的等等。

334

2023.10.17

session失效解决方法
session失效解决方法

session失效通常是由于 session 的生存时间过期或者服务器关闭导致的。其解决办法:1、延长session的生存时间;2、使用持久化存储;3、使用cookie;4、异步更新session;5、使用会话管理中间件。

775

2023.10.18

cookie与session的区别
cookie与session的区别

本专题整合了cookie与session的区别和使用方法等相关内容,阅读专题下面的文章了解更详细的内容。

97

2025.08.19

数据库Delete用法
数据库Delete用法

数据库Delete用法:1、删除单条记录;2、删除多条记录;3、删除所有记录;4、删除特定条件的记录。更多关于数据库Delete的内容,大家可以访问下面的文章。

287

2023.11.13

drop和delete的区别
drop和delete的区别

drop和delete的区别:1、功能与用途;2、操作对象;3、可逆性;4、空间释放;5、执行速度与效率;6、与其他命令的交互;7、影响的持久性;8、语法和执行;9、触发器与约束;10、事务处理。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

222

2023.12.29

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

4

2026.03.10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.5万人学习

Rust 教程
Rust 教程

共28课时 | 6.7万人学习

Git 教程
Git 教程

共21课时 | 4.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号