You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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钩子捕获时长统计数据:

  1. 在项目根目录创建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)
  1. 运行测试时带上--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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 03:04:53