如何编写复杂的`pytest`跳过装饰器?

问题描述 投票:0回答:2

从文档来看,编写跳过装饰器(您可以从例如

conftest.py
导入)的预期方法似乎是使用
skipif
https://docs.pytest.org/en/6.2.x) /skipping.html#id1)。但是,示例中显示的条件很简单,不需要与任何其他参数化交互。当您还需要检查参数作为要跳过的条件的一部分时,是否可以让
skipif
工作?

https://github.com/scipy/scipy/blob/4a6db1500dc62870865fe7827524abd332a88fd9/scipy/conftest.py#L147-L180中,我们有装饰器可以正确执行我们想要的跳过,但是,(IIUC)因为我们使用

 skip
而不是
skipif
,当我们使用
-rsx
时,跳跃被报告为来自
conftest.py
,例如
SKIPPED [27] scipy/conftest.py:159: is_isomorphic only supports NumPy backend
。有没有一种方法可以编写装饰器,以便从它们起源的测试中报告跳过?

我们可以从

--verbose
恢复该信息,但如果这与
-rsx
一起使用会容易得多。干杯!

# conftest.py
array_api_compatible = pytest.mark.parametrize("xp", xp_available_backends.values())

def skip_if_array_api_gpu(func):
    reason = "do not run with Array API on and not on CPU"
    # method gets there as a function so we cannot use inspect.ismethod
    if '.' in func.__qualname__:
        @wraps(func)
        def wrapped(self, *args, **kwargs):
            xp = kwargs["xp"]
            if SCIPY_ARRAY_API and SCIPY_DEVICE != 'cpu':
                if xp.__name__ == 'cupy':
                    pytest.skip(reason=reason)
                elif xp.__name__ == 'torch':
                    if 'cpu' not in torch.empty(0).device.type:
                        pytest.skip(reason=reason)
            return func(self, *args, **kwargs)
    else:
        @wraps(func)
        def wrapped(*args, **kwargs):
            # ditto
            return func(*args, **kwargs)
    return wrapped
# example test
@skip_if_array_api_gpu
@array_api_compatible
def test_xxx(xp):
   ...

x-ref https://github.com/pytest-dev/pytest/discussions/11726

python oop testing pytest decorator
2个回答
0
投票

这是一个示例测试,显示

skipif
仅由于全局条件而跳过,但您可以生成自己的跳过包装器,该包装器会检测某些条件并继续进行测试,或者引发
skip()
报告测试已跳过。

import pytest
from pytest import skip
import functools

global_int = 2


def skipIfNotDynamic(test_method):
    @functools.wraps(test_method)
    def wrapper(self, **kwargs):
        xp = kwargs["xp"]
        if not xp:
            raise skip(f"Skip because xp is Falsey during {test_method.__name__}")

        return test_method(self, **kwargs)

    return wrapper


array_api_compatible = pytest.mark.parametrize('xp', [1, 2, 0, 3])


class TestGroup:
    @pytest.mark.skipif(global_int == 2, reason='global control')
    def test_something(self):
        assert True == False  # add assertion here

    @skipIfNotDynamic
    @array_api_compatible
    def test_else(self, xp):
        assert xp == 0, "test_else"

输出:

F:\...>pytest -rsx soFunctionsEmulatingSkipif.py
================================================= test session starts =================================================
platform win32 -- Python 3.11.5, pytest-7.4.3, pluggy-1.3.0
rootdir: F:\...
collected 5 items

soFunctionsEmulatingSkipif.py sFFsF                                                                              [100%]

====================================================== FAILURES =======================================================
_______________________________________________ TestGroup.test_else[1] ________________________________________________

self = <soFunctionsEmulatingSkipif.TestGroup object at 0x0000020F489545D0>, xp = 1

    @skipIfNotDynamic
    @array_api_compatible
    def test_else(self, xp):
>       assert xp == 0, "test_else"
E       AssertionError: test_else
E       assert 1 == 0

soFunctionsEmulatingSkipif.py:32: AssertionError
_______________________________________________ TestGroup.test_else[2] ________________________________________________

self = <soFunctionsEmulatingSkipif.TestGroup object at 0x0000020F48954B10>, xp = 2

    @skipIfNotDynamic
    @array_api_compatible
    def test_else(self, xp):
>       assert xp == 0, "test_else"
E       AssertionError: test_else
E       assert 2 == 0

soFunctionsEmulatingSkipif.py:32: AssertionError
_______________________________________________ TestGroup.test_else[3] ________________________________________________

self = <soFunctionsEmulatingSkipif.TestGroup object at 0x0000020F48920E90>, xp = 3

    @skipIfNotDynamic
    @array_api_compatible
    def test_else(self, xp):
>       assert xp == 0, "test_else"
E       AssertionError: test_else
E       assert 3 == 0

soFunctionsEmulatingSkipif.py:32: AssertionError
=============================================== short test summary info ===============================================
SKIPPED [1] soFunctionsEmulatingSkipif.py:25: global control
SKIPPED [1] soFunctionsEmulatingSkipif.py:14: Skip because xp is Falsey during test_else
============================================ 3 failed, 2 skipped in 0.80s =============================================

0
投票

请参阅 https://github.com/pytest-dev/pytest/issues/11742#issuecomment-1937678284 了解我们的解决方案。我们最终使用了标记和固定装置。

© www.soinside.com 2019 - 2024. All rights reserved.