自定义 pytest junitxml 失败报告

Posted

技术标签:

【中文标题】自定义 pytest junitxml 失败报告【英文标题】:Customizing pytest junitxml failure reports 【发布时间】:2015-06-14 02:01:23 【问题描述】:

我正在尝试反省测试失败并将其他数据包含到 junit xml 测试报告中。具体来说,这是对外部产品的一套功能测试,我想将产品的日志包含在故障报告中。

使用找到的方法here,我能够在执行最终显示在 jenkin 的失败报告中的多路调用之前将日志打印到标准输出。但我确信有更好的方法来实现这一点。

我尝试使用 pytest_runtest_logreport 挂钩将日志附加到“sections”属性中,该属性已经包含“捕获的标准输出”和“捕获的标准错误”流。但是新添加的部分不会进入 xml 文件。我也在 pytest_runtest_makereport 钩子中直接尝试了上述技术,结果相似。

pytest 2.7 的发行说明指出,2.8 将放弃使用多调用支持,@pytest.mark.hookwrapper 是实现它的新方法,但我似乎根本无法做到这一点 - “ yield" 返回 None 而不是 CallOutcome 对象(在 makereport 挂钩中尝试过)。即使它返回了一些东西,我也不确定我是否可以添加一些会在 xml 报告中显示的东西。

是否有任何我缺少的功能可以让我以灵活的方式做到这一点? (灵活的意思是:不绑定到标准输出或记录调用,如捕获日志插件)

【问题讨论】:

【参考方案1】:

编辑:由于我需要访问测试项目的 funcargs(和测试结果)以进行报告,因此我能够将逻辑移动到 pytest_runtest_makereport(item, __multicall__) 挂钩。诀窍是执行多重调用,它返回报告对象:

@pytest.mark.tryfirst
def pytest_runtest_makereport(item, call, __multicall__):
    report = __multicall__.execute()
    # then I was able to manipulate report and get the same results as below

Bruno 的回答给了我更彻底分析此功能所需的动力:)

下面是它的工作原理:

def pytest_runtest_logreport(report):
    if report.failed:
        report.longrepr.sections.append(("Header", "Message", "-"))
        report.sections.append(("Captured stdout", "This is added to stdout"))
        report.sections.append(("Captured stderr", "This is added to stderr"))
        report.sections.append(("Custom Section", "This can only be seen in the console - the xml won't have it."))

longrepr 属性仅在发生故障时可用。它需要一个 3 元组,最后一个值是用于装饰装饰/环绕标题的字符。它将出现在报告的“失败”部分:

----------------------------------- Header ------------------------------------
Message

自定义部分将创建额外的结果部分以打印到控制台。但是他们不会进入junitxml:

------------------------------- Custom Section --------------------------------
This can only be seen in the console - the xml won't have it.

junitxml 报告只有 2 个部分:out 和 err。要向其中添加自定义文本,您必须创建名为“Captured std”的部分,并且只有这些部分才能将其添加到 xml 文件中。任何其他名称都将生成一个仅在控制台中可见的自定义部分。

这是使用上面代码生成的 junitxml,为了这篇文章,进行了一些重新格式化:

<?xml version="1.0" encoding="utf-8" ?> 
<testsuite errors="0" failures="1" name="pytest" skips="0" tests="1" time="0.646">
  <testcase classname="test_reporting" name="test_fail" time="0.000999927520752">
    <failure message="test failure">
      @ut def test_fail(): > assert 0, "It failed"
      E AssertionError: It failed 
      E assert 0 test_reporting.py:346: AssertionError
      ----------------------------------- Header ------------------------------------
      Message
    </failure> 
    <system-out>This is added to stdout</system-out> 
    <system-err>This is added to stderr</system-err> 
  </testcase>
</testsuite>

【讨论】:

此解决方案现在会为 __multicall__ 参数生成弃用警告。我想知道是否有办法很好地避免它。我唯一能做的就是用from _pytest.runner import pytest_runtest_makereport as _pytest_runtest_makereport调用这个钩子的pytest原始实现来获取报告 是的,您可以通过为输出报告屈服来避免 __multicall__ 弃用警告。在pytest_runtest_logreport() 里面尝试:outcome = yield 然后report = outcome.get_result() `【参考方案2】:

要向测试报告(XML、控制台或其他)添加信息,请查看reporting hooks,更具体地说是pytest_runtest_logreport。

【讨论】:

我并不是要让这个问题针对junit。我真正需要的是一种将任意数据从代码中注入到 pytest 的故障报告中的方法。日志记录方法有效(这是我正在使用的),但阻止我使用“-s”开关(使用它会使 pytest 不捕获标准输出,因此日志记录到控制台根本不会使其成为 junit 报告)。 哦,我明白了。然后更新了我的答案。 Tnx ;) 但是这个钩子已经在我原来的帖子中提到过......我想我用错了吗? 很抱歉。您可以发布一个示例以及您的期望吗?那个钩子应该够了。 我终于有时间分析它并发布了我的发现。【参考方案3】:

pytest_runtest_makereport(item, call)@pytest.hookimpl(hookwrapper=True) 一起使用允许在报告用于创建xml 之前访问报告,例如

@pytest.hookimpl(hookwrapper=True)                                                                                                                                                                                                                                                                                         
def pytest_runtest_makereport(item, call):
    result = yield                                                                                                                                                                                                                                                                                         
    if result.get_result().failed:
        report.longrepr.addsection("Header","message")

使用 pytest-3.9.3

虽然 hookwrapper 的 the documentation 确实建议谨慎

如果底层钩子的结果是一个可变对象,他们可能会修改该结果,但最好避免它。

【讨论】:

以上是关于自定义 pytest junitxml 失败报告的主要内容,如果未能解决你的问题,请参考以下文章

[接口测试_B] 07 Pytest的测试报告

pytest执行并生成报告方式

Python测试框架pytest(16)运行上次失败用例查看与清除缓存cache自定义标记mark

Pytest测试框架知识

8pytest -- 捕获告警信息

pytest学习和使用16-HTML报告如何生成?(pytest-html)