在pytest parametrize中标记输入
我有一个测试,想把它放在两个不同的测试套件里运行,但每个套件需要不同的参数输入。测试套件是通过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或skip(可以参考http://pytest.org/latest/skipping.html上的“Skip/xfail with parametrize”)。如果能写一个条件语句,只在运行B套件时为真,那也能满足我的需求。
提前谢谢你的帮助。
3 个回答
你使用了 @pytest.mark.suiteX 这个标记,把所有的参数化测试都标记上了,这样实际上你就给所有的测试都加上了这个标记。其实应该只在参数列表里面加标记:
import pytest
mark = pytest.mark
@mark.parametrize("inputParameter", [
mark.suiteA(10),
mark.suiteB(12),
])
def test_printInputParameter(inputParameter):
print inputParameter
然后在命令行中,使用 -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
看起来你可以使用skipif标记来实现这个功能,具体的说明可以参考这个链接:http://pytest.org/latest/skipping.html#skip-xfail-with-parametrize(你提到的那个)。你只需要知道你的代码是运行在suiteA还是suiteB,这个你可能已经有办法判断,因为你已经在使用suiteA
和SuiteB
标记了。为了举个例子,我们可以在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模块上设置一个属性确实是个不太优雅的解决办法,但在这种情况下,它是个简单的解决方案。
你走在正确的道路上。问题的一部分在于,你给测试函数同时加了 suiteA 和 suiteB 的标记,这样这个函数就被认为是两个测试套件的一部分。
你现在使用的参数标记方式在 '14 版本时可能有效(在 pytest 3 版本中也能用),但在最新的 pytest 版本(5.x)中就不行了。
这些例子使用的是最新的标记方式,这种方式至少在 pytest 3.x 版本中也能用。
根据 pytest 文档:
使用 parametrize 时,应用一个标记会使其作用于每个单独的测试。不过,也可以将标记应用于单个测试实例:
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 则只会用标记的参数运行这个测试。
所以,对于你的例子,你可以这样做(请原谅我,除了标记外,我把所有名字都更新成了 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 下用所有参数运行。
这个替代方案在你的例子中是可行的,但如果你有任何不重叠的参数(例如 13 在 suiteB 下 不 被运行),你就必须像中间的例子那样逐个指定它们。