如何让pytest-stress在pytest-html中按迭代单独展示测试结果?
问题
我需要用pytest-stress按时长运行测试,之前用的是按迭代次数运行的pytest-repeat,并且通过pytest-html生成测试报告。切换到pytest-stress后,所有测试迭代结果都合并在同一行,但pytest-repeat能把每个迭代的测试结果单独分行展示。我希望pytest-stress的每个测试迭代在pytest-html里单独成行,格式类似test_report.py::TestReport::test_fail[1]。
已知pytest-stress重写了会话级别的pytest_runtestloop钩子,我尝试参考pytest-repeat中重写函数级别pytest_generate_tests的逻辑修改了conftest.py,但没成功。现在想知道能不能通过微调conftest.py实现需求,还是必须自定义pytest插件。
示例测试代码
import logging class TestReport: def test_pass(self): logging.info("assert(True)") assert(True) def test_fail(self): logging.info("assert(False)") assert(False)
已修改的conftest.py代码
def pytest_html_results_table_header(cells): cells.insert(2, html.th("Time", class_="sortable time", col="time")) cells.pop() def pytest_html_results_table_row(report, cells): cells.insert(2, html.td(datetime.utcnow(), class_="col-time")) cells.pop() @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield report = outcome.get_result() setattr(report, "duration_formatter", "%H:%M:%S.%f") @pytest.hookimpl(tryfirst=True) def pytest_configure(config): if not os.path.exists('results'): os.makedirs('results') config.option.htmlpath = 'results/results_'+datetime.now().strftime("%Y-%m-%d_T%H-%M-%S")+".html" #change name based on iteration def pytest_itemcollected(item): cls = item.getparent(pytest.Class) iter = 0 if hasattr(cls.obj, "iteration"): iter = int(getattr(cls.obj, "iteration")) item._nodeid = item._nodeid + f"[{iter}]"
解决方案
可以通过微调conftest.py实现需求,不需要自定义插件。核心思路是在pytest-stress的迭代循环中,为每个测试实例注入迭代编号,并修改测试报告的节点ID,让pytest-html识别为不同的测试条目。
具体修改步骤如下:
- 用会话级变量跟踪当前迭代次数
- 在测试运行前动态修改节点ID,添加迭代编号
- 确保报告使用修改后的节点ID,让
pytest-html将每个迭代视为独立条目
修改后的conftest.py代码:
import os import datetime import pytest from py.xml import html def pytest_configure(config): if not os.path.exists('results'): os.makedirs('results') config.option.htmlpath = 'results/results_'+datetime.now().strftime("%Y-%m-%d_T%H-%M-%S")+".html" # 初始化迭代计数器 config.stress_iteration = 0 def pytest_html_results_table_header(cells): cells.insert(2, html.th("Time", class_="sortable time", col="time")) cells.pop() def pytest_html_results_table_row(report, cells): cells.insert(2, html.td(datetime.utcnow(), class_="col-time")) cells.pop() @pytest.hookimpl(hookwrapper=True) def pytest_runtest_protocol(item, nextitem): session = item.session # 每次测试运行前递增迭代计数器 session.config.stress_iteration += 1 original_nodeid = item.nodeid # 为当前测试实例添加迭代编号 item.nodeid = f"{original_nodeid}[{session.config.stress_iteration}]" try: yield finally: # 恢复原始节点ID,避免干扰后续逻辑 item.nodeid = original_nodeid @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield report = outcome.get_result() setattr(report, "duration_formatter", "%H:%M:%S.%f") # 确保报告使用带迭代编号的节点ID report.nodeid = item.nodeid
说明:
pytest_configure中初始化stress_iteration变量,用于跟踪迭代次数pytest_runtest_protocol在每次测试运行前修改节点ID,执行完成后恢复原始ID,避免影响其他测试流程pytest_runtest_makereport让报告使用修改后的节点ID,这样pytest-html会将每个迭代视为独立测试条目,单独分行展示
修改后,测试迭代结果会以test_report.py::TestReport::test_fail[1]、test_report.py::TestReport::test_fail[2]的形式在报告中单独显示。
内容的提问来源于stack exchange,提问作者anogowski
相关产品推荐
相关产品推荐

