如何用自定义字符串完全替换 pytest 输出以进行测试?

Posted

技术标签:

【中文标题】如何用自定义字符串完全替换 pytest 输出以进行测试?【英文标题】:How to replace pytest output for test completely with a custom string? 【发布时间】:2019-07-05 12:46:45 【问题描述】:

场景

我正在编写一个包,它要求我从通过subprocess 运行的pytest 内部调用pytest。所以很明显,从子进程捕获的输出正是我想要显示的错误,因为它具有 pytest 提供的所有很好的格式和信息。不幸的是,目前主要的 pytest 调用只显示我的包装器的内部代码,而不是漂亮的子进程输出,在我打印它之后,它只显示在 pytest 的捕获的 stdout 部分中。 我想格式化失败和错误的输出,就像直接调用代码一样,并隐藏进行了子进程调用。因此,我基本上想用不同的字符串完全替换一个测试函数的输出。这可能吗?

MWE

让我们看一个简单包装函数的 MWE(没有做任何有用的事情,但是我能想到的最短的 MWE):

import functools
from subprocess import Popen, PIPE
import sys


def call_something(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # for the sake of simplicity, a dummy call
        p = Popen([sys.executable, '-c', 'import numby'], stderr=PIPE, stdout=PIPE)
        # let's imagine this is the perfect string output we want
        # to print instead of the actual output generated from...
        error = p.communicate()[1].decode("utf-8")

        if p.returncode != 0:
            # ... this line
            raise AssertionError(f'error')

    return wrapper

@call_something
def test_something():
    assert 1 == 2

如您所见,我的 test_something() 函数在子进程中将失败。

电流输出

如果我用这个文件运行pytest,我会得到:

================================== FAILURES ===================================
_______________________________ test_something ________________________________

func = <function test_something at 0x000001EA414F1400>, args = (), kwargs = 
p = <subprocess.Popen object at 0x000001EA414A67B8>
error = 'Traceback (most recent call last):\r\n  File "<string>", line 1, in <module>\r\nModuleNotFoundError: No module named \'numby\'\r\n'

    def wrapper(*args, **kwargs):
        # for the sake of simplicity, a dummy call
        p = Popen([sys.executable, '-c', 'import numby'], stderr=PIPE, stdout=PIPE)
        # let's imagine this is the perfect string output we want
        # to print instead of the actual output generated from...
        error = p.communicate()[1].decode("utf-8")

        if p.returncode != 0:
            # ... this line
>               raise AssertionError(f'error')
E               AssertionError: Traceback (most recent call last):
E                 File "<string>", line 1, in <module>
E               ModuleNotFoundError: No module named 'numby'

test_me.py:18: AssertionError
========================== 1 failed in 0.18 seconds ===========================

显然,我不想展示包装函数的细节。而是

期望的输出

我想展示子流程中发生的事情。所以它应该看起来像这样(或类似)。

================================== FAILURES ===================================
_______________________________ test_something ________________________________

<string captured from subprocess>
========================== 1 failed in 0.11 seconds ===========================

所以我的问题很小:

简短的问题

如何用自定义字符串替换用于测试的 pytest 输出 来自我的包装?

【问题讨论】:

您找到解决问题的方法了吗? 很遗憾没有:( 【参考方案1】:

当引发错误时,您可以使用from 语法来抑制或更改异常的链接方式。例如,考虑以下情况:

try:
    a / b
except ZeroDivisionError:
    raise ValueError('Invalid b value')

这将显示一个ZeroDivisionError,如果b == 0 后跟一个ValueError,但您可能想要隐藏ZeroDivisionError,因为唯一相关的部分是ValueError。所以你应该写:

try:
    a / b
except ZeroDivisionError:
    raise ValueError('Invalid b value') from None

这只会显示ValueError,告诉你b错了

您还可以使用from 语法执行其他操作,这与此处相关,请参阅this thread 了解详细信息。

我通过抑制奇怪的管道错误并改为提高 ModuleNotFoundError 来做与您正在尝试的非常相似的事情:

# Test if <module> is callable
try:
    sp.run('<run some module>', shell=True, check=True, stdout=sp.PIPE)
except sp.CalledProcessError as exc:
    raise ModuleNotFoundError(
        '<some module> not working. Please ensure it is installed and can be called with the command: <command>'
    ) from exc

注意这里使用from exc

【讨论】:

但是,pytest 总是会解析您引发的异常,并查看它发生在哪一行,以及传递给测试函数的参数。所有这些都在返回的字符串中,所以我想替换 ____...=====... 之间的 整个 pytest 输出

以上是关于如何用自定义字符串完全替换 pytest 输出以进行测试?的主要内容,如果未能解决你的问题,请参考以下文章

Java 类 TrayIcon,通知消息。如何用自定义声音替换系统声音?

如何用自定义 <comment> 元素替换 HTML 注释

Cordova android - 如何用生成的替换自定义 build.gradle

如何用不同的 Xib 替换我的自定义视图

如何用自定义变量替换 DJANGO 中 JAVASCRIPT 脚本生成的 Google Map Key

如何用自定义alterna替换管理标题中的默认WordPress-W-logo