如何在pytest的HTML/JUnit-XML报告中捕获时长统计信息
问题描述
我通过pytest命令行的--durations选项提取最慢的5个测试的时长信息,测试结束后控制台会输出如下统计内容:
======== fixture duration top =================================================== total name num avg min 0:00:01.219000 login_token 1 0:00:01.219000 0:00:01.219000 0:00:00.734000 login_creds 1 0:00:00.734000 0:00:00.734000 0:00:01.953000 grand total 18 0:00:00.734000 0:00:00 ======== test call duration top ================================================= total name num avg min 0:13:19.203000 Test_some_feature::test_01 8 0:01:38.351500 0:01:35.562000 0:04:19.500000 Test_some_feature::test_02 1 0:04:19.500000 0:04:19.500000 0:03:42.329000 Test_some_feature::test_03 8 0:00:25.195500 0:00:23.281000 0:21:21.032000 grand total 17 0:01:38.351500 0:00:23.281000 ========= test setup duration top =============================================== total name num avg min 0:00:00 grand total 17 0:00:00 0:00:00 ========= test teardown duration top ============================================ total name num avg min 0:00:00 grand total 17 0:00:00 0:00:00 ========= slowest 15 durations ==================================================
目前测试结果输出为HTML或JUnit-XML报告,想知道是否存在钩子可以将这些时长统计信息捕获到报告中?试过相关解决方案但报告未发生任何变化,求可行的解决建议。
可行解决建议
方案1:自定义插件捕获时长并注入pytest-html报告
利用pytest-html的pytest_html_results_table_html钩子修改HTML报告内容,结合pytest_terminal_summary钩子捕获时长统计数据:
- 在项目根目录创建
conftest.py文件,添加以下代码:
import pytest from _pytest.terminal import TerminalReporter # 存储时长统计的全局变量 duration_stats = {} def pytest_terminal_summary(terminalreporter: TerminalReporter, exitstatus, config): # 捕获fixture、测试调用等时长统计 if hasattr(terminalreporter, '_durationtotals'): duration_stats['fixture'] = terminalreporter._durationtotals.get('fixture', []) duration_stats['test_call'] = terminalreporter._durationtotals.get('call', []) duration_stats['setup'] = terminalreporter._durationtotals.get('setup', []) duration_stats['teardown'] = terminalreporter._durationtotals.get('teardown', []) def pytest_html_results_table_html(report, data): # 仅在最终报告生成时添加统计 if report.when == 'teardown' and not report.failed: html = '<h2>慢测试时长统计</h2>' # 添加fixture时长表格 if duration_stats.get('fixture'): html += '<h3>Fixture 耗时Top</h3>' html += '<table border="1"><tr><th>总耗时</th><th>名称</th><th>调用次数</th><th>平均耗时</th><th>最小耗时</th></tr>' for item in duration_stats['fixture']: html += f'<tr><td>{item.total}</td><td>{item.name}</td><td>{item.num}</td><td>{item.avg}</td><td>{item.min}</td></tr>' html += '</table>' # 添加测试调用时长表格 if duration_stats.get('test_call'): html += '<h3>测试调用耗时Top</h3>' html += '<table border="1"><tr><th>总耗时</th><th>测试名称</th><th>调用次数</th><th>平均耗时</th><th>最小耗时</th></tr>' for item in duration_stats['test_call']: html += f'<tr><td>{item.total}</td><td>{item.name}</td><td>{item.num}</td><td>{item.avg}</td><td>{item.min}</td></tr>' html += '</table>' data.append(html)
- 运行测试时带上
--durations=5和--html=report.html参数,生成的HTML报告末尾会自动添加时长统计模块。
方案2:修改JUnit-XML报告(添加自定义节点)
通过pytest_junitxml_make_report钩子,将时长统计作为附加节点写入JUnit-XML:
在conftest.py中添加:
import pytest from _pytest.terminal import TerminalReporter duration_stats = {} def pytest_terminal_summary(terminalreporter: TerminalReporter, exitstatus, config): if hasattr(terminalreporter, '_durationtotals'): duration_stats['fixture'] = terminalreporter._durationtotals.get('fixture', []) duration_stats['test_call'] = terminalreporter._durationtotals.get('call', []) def pytest_junitxml_make_report(report, config): xml_report = getattr(report, 'xml_report', None) if xml_report is not None and report.when == 'teardown': suite_node = xml_report.node.parent if 'duration_stats_added' not in suite_node.attrib: stats_node = suite_node.append('duration_stats') # 添加fixture统计 fixture_node = stats_node.append('fixture_top') for item in duration_stats.get('fixture', []): item_node = fixture_node.append('fixture') item_node.set('name', item.name) item_node.set('total', str(item.total)) item_node.set('num', str(item.num)) item_node.set('avg', str(item.avg)) # 添加测试调用统计 test_call_node = stats_node.append('test_call_top') for item in duration_stats.get('test_call', []): item_node = test_call_node.append('test') item_node.set('name', item.name) item_node.set('total', str(item.total)) item_node.set('num', str(item.num)) item_node.set('avg', str(item.avg)) suite_node.attrib['duration_stats_added'] = 'true'
运行测试时带上--durations=5和--junitxml=report.xml参数,生成的XML报告会包含<duration_stats>节点存储统计数据。
方案3:直接捕获控制台输出(兼容全版本pytest)
如果因pytest版本差异导致属性名变化,可直接捕获控制台输出的统计内容:
import pytest from io import StringIO captured_output = StringIO() def pytest_terminal_summary(terminalreporter, exitstatus, config): # 重定向终端输出到缓冲区 original_write = terminalreporter._tw.write def capture_write(s, **kwargs): captured_output.write(s) original_write(s, **kwargs) terminalreporter._tw.write = capture_write def pytest_html_results_table_html(report, data): if report.when == 'teardown': # 提取时长统计段落 output = captured_output.getvalue() duration_section = output.split('======== fixture duration top ===================================================')[1].split('========= slowest 15 durations ==================================================')[0] # 转为HTML代码块插入报告 html = '<h2>慢测试时长统计</h2><pre>' + duration_section + '</pre>' data.append(html)
内容的提问来源于stack exchange,提问作者Archie
相关产品推荐
相关产品推荐

