如何在多个测试函数之间共享参数化参数?

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

我在同一个文件中有许多测试函数,它们共享一组通用的参数化参数,如下面的代码片段所示。当测试函数的数量增加(>100)时,为每个测试函数重复这些参数似乎效率不高,而且容易出错。 pytest 中是否有任何方法可以定义一组常见的参数化参数并在所有测试中共享它? Fixture 用于注入公共数据依赖。我想知道是否有类似的工具可以注入常见的arguments

import pytest

@pytest.mark.parametrize('common_arg1', [0, 1])
@pytest.mark.parametrize('common_arg2', [2, 3])
@pytest.mark.parametrize('a', [0, 1])
def test_1(common_arg1, common_arg2, a):
    pass
    
@pytest.mark.parametrize('common_arg1', [0, 1])  # repetition
@pytest.mark.parametrize('common_arg2', [2, 3])  # repetition
@pytest.mark.parametrize('b', [0, 1])
def test_2(common_arg1, common_arg2, b):
    pass
    
...

@pytest.mark.parametrize('common_arg1', [0, 1])  # more repetition!
@pytest.mark.parametrize('common_arg2', [2, 3])  # more repetition!
@pytest.mark.parametrize('x', [0, 1])
def test_100(common_arg1, common_arg2, x):
    pass  
pytest
5个回答
44
投票

您可以将所有测试用例移至一个辅助类中,并将通用装饰器放在该类之上。特定于某些测试(方法)的装饰器可以直接应用于它们。检查下面的例子。

import pytest


@pytest.mark.parametrize('common_arg1', [0, 1])
@pytest.mark.parametrize('common_arg2', [2, 3])
class TestParametrized:

    @pytest.mark.parametrize('a', [0, 1])
    def test_1(self, common_arg1, common_arg2, a):
        pass

    @pytest.mark.parametrize('b', [0, 1])
    def test_2(self, common_arg1, common_arg2, b):
        pass

    @pytest.mark.parametrize('x', [0, 1])
    def test_100(self, common_arg1, common_arg2, x):
        pass

12
投票

正如 Kammil 所指出的,最好的方法是拥有一个 Test 类,作为 Kammil 方法的替代方法,是为测试数据提供一个类属性,通过这种方式,您可以拥有不使用 common_arg1 和 common_arg2 的测试方法在你的班级里。但是您仍然需要标记测试,但不需要重写所有参数化参数,并且如果更改类属性的值,它将反映在所有测试中。

如果您要在同一个类中使用不需要 common_arg1 和 common_arg2 的测试方法,这特别有用。

请记住,如果您装饰类,所有方法都必须有 common_arg1 和 common_arg2 作为参数,如果您尝试编写一个不在方法的参数列表中引用它们的方法,您将收到

function uses no argument 'common_arg1'
错误。

因此,为了避免这种情况,你可以这样做:

import pytest

class TestParametrized:

    common_args = ('common_arg1, common_arg2', [([0, 1], [2,3])])

    @pytest.mark.parametrize(*common_args)
    @pytest.mark.parametrize('a', [0, 1])
    def test_1(self, common_arg1, common_arg2, a):
        pass

    @pytest.mark.parametrize(*common_args)
    @pytest.mark.parametrize('b', [0, 1])
    def test_2(self, common_arg1, common_arg2, b):
        pass

    @pytest.mark.parametrize(*common_args)
    @pytest.mark.parametrize('x', [0, 1])
    def test_100(self, common_arg1, common_arg2, x):
        pass

    def test_1000(self):
        pass

注意 test_1000 将会成功,并且不会抱怨没有提及 common_args1 和 common_args2。您还可以通过编写两个类属性将 common_args 一分为二。


7
投票

另一种方法是使用参数化

pytest
夹具。对于单个参数化,很容易使用普通的
pytest
:

import pytest

@pytest.fixture(params=[0, 1])
def common_arg1(request):
    return request.param

@pytest.fixture(params=[2, 3])
def common_arg2(request):
    return request.param

@pytest.mark.parametrize('a', [0, 1])
def test_1(common_arg1, common_arg2, a):
    pass

@pytest.mark.parametrize('b', [0, 1])
def test_2(common_arg1, common_arg2, b):
    pass

@pytest.mark.parametrize('x', [0, 1])
def test_100(common_arg1, common_arg2, x):
    pass

如果您希望以更紧凑的方式编写此内容,您可以使用

pytest_cases.param_fixture

from pytest_cases import param_fixture

common_arg1 = param_fixture('common_arg1', [0, 1])
common_arg2 = param_fixture('common_arg2', [2, 3])

对于多个参数化,您可能希望使用

@pytest_cases.fixture
以便能够使用
@pytest.mark.parametrize
:

import pytest
from pytest_cases import fixture

@fixture
@pytest.mark.parametrize('a', [0, 1])
@pytest.mark.parametrize('b', [2, 3])
def common_arg_multiple(a, b):
    return a * b, a - b

有关详细信息,请参阅pytest-cases文档。 (我是作者;))


5
投票

在 python 中,您几乎可以将任何内容分配给变量,包括 pytest 标记。

import pytest

COMMON_ARG1 = pytest.mark.parametrize('common_arg1', [0, 1])
COMMON_ARG2 = pytest.mark.parametrize('common_arg2', [2, 3])

@COMMON_ARG1
@COMMON_ARG2
@pytest.mark.parametrize('a', [0, 1])
def test_1(common_arg1, common_arg2, a):
    pass
    
@COMMON_ARG1
@COMMON_ARG2
@pytest.mark.parametrize('b', [0, 1])
def test_2(common_arg1, common_arg2, b):
    pass
    
...

@COMMON_ARG1
@COMMON_ARG2
@pytest.mark.parametrize('x', [0, 1])
def test_100(common_arg1, common_arg2, x):
    pass  

当您混合使用常用参数时,这尤其有帮助。

如果它们都相同,就像您的示例一样,最好使用其他答案中所示的测试类,但是如果您有很多以不同组合使用的重复参数,这可以更容易管理。

如果您在不同的测试模块中使用它们,您甚至可以集中它们并从中导入。

 from central_args import COMMON_ARG1, COMMON_ARG2

0
投票

大多数答案都结合了参数的乘积,例如如果有三对参数,则每次测试有 23=8 种组合。例如:

test_1[0-2-0]
test_1[0-2-1]
test_1[0-3-0]
test_1[0-3-1]
test_1[1-2-0]
test_1[1-2-1]
test_1[1-3-0]
test_1[1-3-1]
...
test_100[1-3-1]

但是,如果目标是以不同方式组合参数,请将数据放入字典中并创建一些命名参数化测试:

import pytest

common_params = {
    "one": {
        "common_arg1": 0,
        "common_arg2": 2,
        "a": 0,
        "b": 0,
        "x": 0,
    },
    "two": {
        "common_arg1": 1,
        "common_arg2": 3,
        "a": 1,
        "b": 1,
        "x": 1,
    },
}

@pytest.mark.parametrize(
    "common_arg1, common_arg2, a",
    [
        pytest.param(val["common_arg1"], val["common_arg2"], val["a"], id=key)
        for key, val in common_params.items()
    ],
)
def test_1(common_arg1, common_arg2, a):
    pass


@pytest.mark.parametrize(
    "common_arg1, common_arg2, b",
    [
        pytest.param(val["common_arg1"], val["common_arg2"], val["b"], id=key)
        for key, val in common_params.items()
    ],
)
def test_2(common_arg1, common_arg2, b):
    pass


@pytest.mark.parametrize(
    "common_arg1, common_arg2, x",
    [
        pytest.param(val["common_arg1"], val["common_arg2"], val["x"], id=key)
        for key, val in common_params.items()
    ],
)
def test_100(common_arg1, common_arg2, x):
    pass

根据每个测试的需要运行测试参数“一个”或“两个”:

test_1[one]
test_1[two]
test_2[one]
test_2[two]
test_100[one]
test_100[two]
© www.soinside.com 2019 - 2024. All rights reserved.