在pytest参数化中标记输入

时间:2014-07-11 12:58:58

标签: pytest

我有一个测试,我希望作为两个不同测试套件的一部分运行,并根据测试套件提供不同的参数输入。测试套件用pytest标记识别。

有没有办法标记参数化条目,以便它们只在特定的测试套件中运行?

以下是我想做的事情:

@pytest.mark.suiteA # include the test in Suite A
@pytest.mark.suiteB # include the test in Suite B
@pytest.mark.parametrize("inputParameter", [
                              (10),                    # use this input for Suites A and B
                              pytest.mark.suiteB(12)]) # only use this input for Suite B
def test_printInputParameter(inputParameter):
    print inputParameter

运行这样的代码不会产生我想要的结果 - 两个输入都用于两个套件。

我已经看到pytest将允许在参数化中使用xfail或跳过(参见http://pytest.org/latest/skipping.html上的“使用参数化跳过/ xfail”)如果有办法编写条件语句,只有在运行Suite B,也可以实现我的需要。

提前感谢您的帮助。

3 个答案:

答案 0 :(得分:1)

您处在正确的轨道上。问题的一部分是您已经用suiteA和suiteB标​​记了测试功能,因此该功能被视为两者的一部分。

您正在使用的参数标记样式可能在'14时代就可以使用(直到pytest 3仍可使用),但在最新的pytest版本(5.x)中则无效。

这些示例使用最新样式,该样式至少也可以追溯到pytest 3.x。

来自the pytest docs

使用参数设置时,应用标记将使其应用于每个单独的测试。但是,也可以将标记应用于单个测试实例:

import pytest


@pytest.mark.foo
@pytest.mark.parametrize(
    ("n", "expected"), [(1, 2), pytest.param(1, 3, marks=pytest.mark.bar), (2, 3)]
)
def test_increment(n, expected):
    assert n + 1 == expected

在上面的示例中,使用标记 foo 将运行该测试,所有参数(包括bar)都将被测试。使用标记 bar 将仅使用标记为1的参数运行该测试。

因此,以您的示例为例,您可以这样做(请原谅,我将除标记之外的所有名称都更新为PEP8标准):

@pytest.mark.parametrize("input_parameter", [
        # use this input for Suite A and Suite B
        pytest.param(10, marks=[pytest.mark.suiteA, pytest.mark.suiteB]), 
        # use this input only for Suite B
        pytest.param(12, marks=pytest.mark.suiteB),
        # this input will run when no markers are specified
        (13),
]) 
def test_print_input_parameter(input_parameter):
    print(input_parameter)

您必须摆脱函数上方的两个标记修饰符。您只需要此参数修饰符。

根据注释,将输入10标记为可确保仅与suiteA或suiteB一起运行。如果需要调用其中一个或两个,它将执行。

输入12绑定到单个标记suiteB。仅在需要suiteB时才会执行。

我还添加了输入值13作为默认未标记测试运行的示例。正常情况下,这不会对suiteA或suiteB(或suiteC或任何其他标记过滤器)执行,但是如果未指定标记(其余的标记),它将运行。

或者,您可以执行以下操作:

@pytest.mark.suiteB # this function is a part of suiteB
@pytest.mark.parametrize("input_parameter", [
        # use this input for Suite A and Suite B
        pytest.param(10, marks=pytest.mark.suiteA), 
        # use this input only for Suite B
        (12),
        # this input is also part of Suite B thanks to the function decorator, as well as suiteC and suiteD
        pytest.param(13, marks=[pytest.mark.suiteC, pytest.mark.suiteD]),
]) 
def test_print_input_parameter(input_parameter):
    print(input_parameter)

使用原始的两个参数,您实际上进行了完整的suiteB测试,其中一个参数仅适用于suiteA。

在这种情况下,函数装饰器在suiteB下运行整个测试。如果指定suiteA,将仅执行10个。

因为使用了函数装饰器,所以我组成的参数13像该函数的所有参数一样,也是suiteB的一部分。我可以根据需要将其添加到其他标记中,但是函数装饰器可确保此测试将在suiteB下使用所有参数运行。

在您的示例中,该替代方法也可以使用,但是如果您有任何不重叠的参数(例如,在suiteB下运行13是 not ),则必须像中间的例子。

答案 1 :(得分:0)

好像你可以使用http://pytest.org/latest/skipping.html#skip-xfail-with-parametrize(你所指的)中描述的skipif标记来做到这一点。您需要做的就是知道您的代码是否在suiteA或suiteB中运行,您可能已经拥有suiteASuiteB标记。因此,对于示例,让我们在sys模块上设置一个(丑陋的)辅助属性,就像检测代码是否在py.test下运行一样:

# E.g. in conftest.py; in real life the value probably comes from a
# command line option added by pytest_addoption()
def pytest_configure(config):
    sys._my_test_suite = 'A'  # or 'B'

# The actual test can now use this in skipif
@pytest.mark.suiteA # include the test in Suite A
@pytest.mark.suiteB # include the test in Suite B
@pytest.mark.parametrize(
    "inputParameter",
    [(10), pytest.mark.skipif(sys._my_test_suite == 'A', reason='suite A only')(12)])])
def test_printInputParameter(inputParameter):
    print inputParameter

是的,在sys模块上设置一个属性是一个丑陋的黑客,但对于这种情况它是一个简单的解决方案。

答案 2 :(得分:0)

使用@ pytest.mark.suiteX可以使所有参数化测试都带有该标记,因此实际上,您已经标记了所有测试,而仅在参数列表内应用标记:

import pytest

mark = pytest.mark

@mark.parametrize("inputParameter", [
    mark.suiteA(10),
    mark.suiteB(12),
])
def test_printInputParameter(inputParameter):
    print inputParameter

然后在cli上使用-m(标记)选择要过滤的测试:

bash-4.4$ pytest -m suiteA test_in.py -sv
test_in.py::test_printInputParameter[10] 10
PASSED

bash-4.4$ pytest -m suiteB test_in.py -sv
test_in.py::test_printInputParameter[12] 12
PASSED