首页 > 解决方案 > pytest.mark.parametrize 似乎只能 xfail 预期的异常

问题描述

在对引发预期异常的测试进行参数化时,我一直在使用以下形式:

import pytest

class SomeException(Exception):
    pass

class AnotherException(Exception):
    pass

@pytest.mark.parametrize(
    'error',
    [None,
     pytest.mark.raises(SomeException('the message'),
                        exception=SomeException),
     pytest.mark.raises(AnotherException('the message'),
                        exception=AnotherException),
     pytest.mark.raises(Exception('the message'),
                        exception=Exception),
     ])
def test_mark_raises(error):
    if error:
        raise error

请注意,我已经安装了 pytest-raises 包。好的,所以这行得通,但我最近注意到它正在发出弃用警告:

RemovedInPytest4Warning: Applying marks directly to parameters is deprecated, please use pytest.param(..., marks=...) instead.

好没问题。我们将更新参数化列表。但这一直是个问题。pytest.param 有一个 'marks=' 参数,但传入 pytest.mark.raises 不起作用。我发现在 pytest.param 中使用异常处理的唯一方法(实际上似乎完全有效)如下:

import pytest

class SomeException(Exception):
    pass

class AnotherException(Exception):
    pass

@pytest.mark.parametrize(
    'error',
    [None,
     pytest.param(SomeException('the message'),
                  marks=pytest.mark.xfail(exception=SomeException)),
     pytest.param(AnotherException('the message'),
                  marks=pytest.mark.xfail(exception=AnotherException)),
     pytest.param(Exception('the message'),
                  marks=pytest.mark.xfail(exception=Exception)),
     ])
def test_mark_raises(error):
    if error:
        raise error

好的,所以这似乎确实有效。但是不是通过测试,而是通过测试失败。

我不喜欢这个。如果我期望测试引发特定异常并且它确实引发了异常,那么我会认为测试已经通过,而不是“xfailed”。

如果我正在检查一堆 pytest 结果(在某些情况下超过 1500),从视觉上识别哪些测试是 xfailing 是因为他们期望某种失败条件,哪些是 xfailing 因为它们尚未实现(或其他一些表明我们需要修复某些东西的原因)。

我不喜欢收到一堆警告,但我也想要正确显示它们通过的测试。在预期异常行为的情况下,是否有人知道与 pytest.param 一起使用的正确构造?

标签: pythonexceptionpytest

解决方案


我不确定是回答我自己的问题还是简单地更新问题。由于答案有点冗长,我选择把它放在这里。

pytest-raises 0.10 版现在支持我正在寻找的行为。您现在可以通过以下方式设置异常参数:

import pytest

class SomeException(Exception):
    pass

@pytest.mark.parametrize(
    'error',
    [None,
     pytest.param(SomeException('the message'),
                  marks=pytest.mark.raises(exception=SomeException)),
     ])
def test_mark_raises(error):
    if error:
        raise error

这比以前接受的将标记直接放入参数化列表的做法要冗长一些,但它按预期工作,并且弃用警告消失了。


推荐阅读