测试框架pytest教程(2)-用例依赖库-pytest-dependency

本文介绍了如何使用pytest-dependency插件在pytest中管理测试用例的依赖关系,包括依赖方法的标记、命名、范围设定、跨模块依赖以及处理fixture和参数化测试。插件允许灵活定义和控制测试顺序,但依赖执行顺序敏感。
摘要由CSDN通过智能技术生成

对于 pytest 的用例依赖管理,可以使用 pytest-dependency 插件。该插件提供了更多的依赖管理功能,使你能够更灵活地定义和控制测试用例之间的依赖关系。

Using pytest-dependency — pytest-dependency 0.5.1 documentation

安装 pytest-dependency 插件:

pip install pytest-dependency

基本使用

依赖方法和被依赖方法都需要使用装饰器 @pytest.mark.dependency

在依赖方法装饰器参数列表里填写依赖的用例名称列表

import pytest

@pytest.mark.dependency()
@pytest.mark.xfail(reason="deliberate fail")
def test_a():
    assert False

@pytest.mark.dependency()
def test_b():
    pass

@pytest.mark.dependency(depends=["test_a"])
def test_c():
    pass

@pytest.mark.dependency(depends=["test_b"])
def test_d():
    pass

@pytest.mark.dependency(depends=["test_b", "test_c"])
def test_e():
    pass

 执行结果:2个通过 3个忽略

被依赖的用例执行失败后,依赖的用例不执行,

a执行失败,所以c和e都被忽略了,a也被忽略了。

 为测试用例命名

使用name为测试用例命名,在依赖调用列表可以使用name调用。

import pytest

@pytest.mark.dependency(name="a")
@pytest.mark.xfail(reason="deliberate fail")
def test_a():
    assert False

@pytest.mark.dependency(name="b")
def test_b():
    pass

@pytest.mark.dependency(name="c", depends=["a"])
def test_c():
    pass

@pytest.mark.dependency(name="d", depends=["b"])
def test_d():
    pass

@pytest.mark.dependency(name="e", depends=["b", "c"])
def test_e():
    pass

测试类中的测试方法 

在 pytest 中,可以将测试用例分组到类中。对于测试类中的方法标记依赖关系的方式与简单的测试函数相同。在下面的示例中,我们定义了两个测试类。每个测试类的工作方式与之前的示例相同:

```python
import pytest

@pytest.mark.dependency
class TestClassA:
    def test_a(self):
        assert False

    @pytest.mark.dependency(depends=["TestClassA::test_a"])
    def test_b(self):
        assert True

@pytest.mark.dependency
class TestClassB:
    def test_c(self):
        assert False

    @pytest.mark.dependency(depends=["TestClassB::test_c"])
    def test_d(self):
        assert True
```

在这个示例中,我们定义了两个测试类 `TestClassA` 和 `TestClassB`。每个测试类中的方法都用 `@pytest.mark.dependency` 进行了标记,以指定它们的依赖关系。依赖关系通过传递类名和方法名来指定,格式为 `"TestClass::test_method"`。

这样,你就可以使用测试类来组织和管理测试用例,并使用 `@pytest.mark.dependency` 来标记它们之间的依赖关系。在运行测试时,pytest 将按照定义的依赖关系顺序执行测试方法。

参数化测试用例

import pytest

@pytest.mark.parametrize("x,y", [
    pytest.param(0, 0, marks=pytest.mark.dependency(name="a1")),
    pytest.param(0, 1, marks=[pytest.mark.dependency(name="a2"),
                              pytest.mark.xfail]),
    pytest.param(1, 0, marks=pytest.mark.dependency(name="a3")),
    pytest.param(1, 1, marks=pytest.mark.dependency(name="a4"))
])
def test_a(x,y):
    assert y <= x

@pytest.mark.parametrize("u,v", [
    pytest.param(1, 2, marks=pytest.mark.dependency(name="b1", 
                                                    depends=["a1", "a2"])),
    pytest.param(1, 3, marks=pytest.mark.dependency(name="b2", 
                                                    depends=["a1", "a3"])),
    pytest.param(1, 4, marks=pytest.mark.dependency(name="b3", 
                                                    depends=["a1", "a4"])),
    pytest.param(2, 3, marks=pytest.mark.dependency(name="b4", 
                                                    depends=["a2", "a3"])),
    pytest.param(2, 4, marks=pytest.mark.dependency(name="b5", 
                                                    depends=["a2", "a4"])),
    pytest.param(3, 4, marks=pytest.mark.dependency(name="b6", 
                                                    depends=["a3", "a4"]))
])
def test_b(u,v):
    pass

@pytest.mark.parametrize("w", [
    pytest.param(1, marks=pytest.mark.dependency(name="c1", 
                                                 depends=["b1", "b2", "b6"])),
    pytest.param(2, marks=pytest.mark.dependency(name="c2", 
                                                 depends=["b2", "b3", "b6"])),
    pytest.param(3, marks=pytest.mark.dependency(name="c3", 
                                                 depends=["b2", "b4", "b6"]))
])
def test_c(w):
    pass

运行时依赖

有时,测试实例的依赖关系太复杂,无法使用 pytest.mark.dependency() 标记在运行之前明确地进行公式化。在运行时编译测试的依赖关系列表可能更容易。在这种情况下,pytest_dependency.depends() 函数非常有用。考虑以下示例:

```python
import pytest
from pytest_dependency import depends

@pytest.mark.dependency
def test_a():
    assert False

@pytest.mark.dependency
def test_b():
    depends(test_a())
    assert True
```

在这个示例中,我们使用 pytest_dependency.depends() 函数定义了 test_b() 依赖于 test_a() 的关系。这样,我们可以在运行时根据 test_b() 的需要动态地编译依赖关系列表。

使用 pytest_dependency.depends() 函数时,只需将需要依赖的测试方法作为函数参数传递给它即可。

指明作用范围

scope的默认范围是module,所以基本使用的例子也可以写为如下,

实现效果没有区别,只是指明了范围

import pytest

@pytest.mark.dependency()
@pytest.mark.xfail(reason="deliberate fail")
def test_a():
    assert False

@pytest.mark.dependency()
def test_b():
    pass

@pytest.mark.dependency(depends=["test_a"], scope='module')
def test_c():
    pass

@pytest.mark.dependency(depends=["test_b"], scope='module')
def test_d():
    pass

@pytest.mark.dependency(depends=["test_b", "test_c"], scope='module')
def test_e():
    pass

跨模块需要指明范围为session

如果一个用例依赖的另一个用例在不同的模块,依赖的用例的scope必须是session或者是package。

# test_mod_01.py

import pytest

@pytest.mark.dependency()
def test_a():
    pass

@pytest.mark.dependency()
@pytest.mark.xfail(reason="deliberate fail")
def test_b():
    assert False

@pytest.mark.dependency(depends=["test_a"])
def test_c():
    pass


class TestClass(object):

    @pytest.mark.dependency()
    def test_b(self):
        pass

# test_mod_02.py

import pytest

@pytest.mark.dependency()
@pytest.mark.xfail(reason="deliberate fail")
def test_a():
    assert False

@pytest.mark.dependency(
    depends=["tests/test_mod_01.py::test_a", "tests/test_mod_01.py::test_c"],
    scope='session'
)
def test_e():
    pass

@pytest.mark.dependency(
    depends=["tests/test_mod_01.py::test_b", "tests/test_mod_02.py::test_e"],
    scope='session'
)
def test_f():
    pass

@pytest.mark.dependency(
    depends=["tests/test_mod_01.py::TestClass::test_b"],
    scope='session'
)
def test_g():
    pass

范围为class

测试依赖关系也可以在类范围的级别上定义。这仅适用于测试类中的方法,并将依赖限制为同一类中的其他测试方法。

import pytest

@pytest.mark.dependency()
@pytest.mark.xfail(reason="deliberate fail")
def test_a():
    assert False


class TestClass1(object):

    @pytest.mark.dependency()
    def test_b(self):
        pass


class TestClass2(object):

    @pytest.mark.dependency()
    def test_a(self):
        pass

    @pytest.mark.dependency(depends=["test_a"])
    def test_c(self):
        pass

    @pytest.mark.dependency(depends=["test_a"], scope='class')
    def test_d(self):
        pass

    @pytest.mark.dependency(depends=["test_b"], scope='class')
    def test_e(self):
        pass

 一组测试使用fixture

pytest 在测试用例中对 fixture 实例进行自动分组。如果有一组测试用例,并且需要针对每个测试用例运行一系列的测试,这将非常有用。

例如:

```python
import pytest

# 定义一个测试用例
@pytest.fixture(params=[1, 2, 3])
def test_case(request):
    return request.param

# 运行多次测验
def test_my_tests(test_case):
    assert test_case > 0

def test_other_tests(test_case):
    assert test_case < 10
```

在这个示例中,我们定义了一个名为 `test_case` 的 fixture,它使用 `@pytest.fixture` 装饰器和 `params` 参数来定义一个包含多个测试用例的列表。然后,我们使用 `test_case` fixture 来运行多个测试方法 `test_my_tests` 和 `test_other_tests`。pytest 会自动将这些测试方法与每个测试用例进行匹配,并为每个测试用例运行对应的测试方法。

通过这种方式,我们可以轻松地为每个测试用例执行一系列的测试,而不需要手动为每个测试用例编写独立的测试方法。

使用夹具为用例分组

pytest具有按夹具实例自动分组测试的功能。如果存在一组测试用例,并且对于每个测试用例都需要运行一系列的测试,这一特性尤其有用。



import pytest
from pytest_dependency import depends

@pytest.fixture(scope="module", params=range(1,10))
def testcase(request):
    param = request.param
    return param

@pytest.mark.dependency()
def test_a(testcase):
    if testcase % 7 == 0:
        pytest.xfail("deliberate fail")
        assert False

@pytest.mark.dependency()
def test_b(request, testcase):
    depends(request, ["test_a[%d]" % testcase])
    pass

if __name__ == '__main__':
    pytest.main(["-sv"])

 因为test_a[7]执行失败,所以test_b[7]被跳过。

 如果多个测试方法依赖于一个测试方法,则可以把pytest_dependency.depends()调用单独写一个fixture

import pytest
from pytest_dependency import depends

@pytest.fixture(scope="module", params=range(1,10))
def testcase(request):
    param = request.param
    return param

@pytest.fixture(scope="module")
def dep_testcase(request, testcase):
    depends(request, ["test_a[%d]" % testcase])
    return testcase

@pytest.mark.dependency()
def test_a(testcase):
    if testcase % 7 == 0:
        pytest.xfail("deliberate fail")
        assert False

@pytest.mark.dependency()
def test_b(dep_testcase):
    pass

@pytest.mark.dependency()
def test_c(dep_testcase):
    pass

test_b[7]和test_c[7] 会被跳过,因为test_a[7]失败了。

依赖参数化测试方法

如果一个测试同时依赖于一个参数化测试的所有实例,逐个列出它们在 pytest.mark.dependency() 标记中可能不是最佳解决方案。但是可以根据参数值动态地编译这些列表,如以下示例所示:

import pytest

def instances(name, params):
    def vstr(val):
        if isinstance(val, (list, tuple)):
            return "-".join([str(v) for v in val])
        else:
            return str(val)
    return ["%s[%s]" % (name, vstr(v)) for v in params]


params_a = range(17)

@pytest.mark.parametrize("x", params_a)
@pytest.mark.dependency()
def test_a(x):
    if x == 13:
        pytest.xfail("deliberate fail")
        assert False
    else:
        pass

@pytest.mark.dependency(depends=instances("test_a", params_a))
def test_b():
    pass

params_c = list(zip(range(0,8,2), range(2,6)))

@pytest.mark.parametrize("x,y", params_c)
@pytest.mark.dependency()
def test_c(x, y):
    if x > y:
        pytest.xfail("deliberate fail")
        assert False
    else:
        pass

@pytest.mark.dependency(depends=instances("test_c", params_c))
def test_d():
    pass

params_e = ['abc', 'def']

@pytest.mark.parametrize("s", params_e)
@pytest.mark.dependency()
def test_e(s):
    if 'e' in s:
        pytest.xfail("deliberate fail")
        assert False
    else:
        pass

@pytest.mark.dependency(depends=instances("test_e", params_e))
def test_f():
    pass

test_b, test_d, and test_f will be skipped because they depend on all instances of test_a, test_c, and test_e respectively, but test_a[13], test_c[6-5], and test_e[def] fail. The list of the test instances is compiled in the helper function instances(). 

缺点

依赖用例执行顺序

这个库非常依赖用例的执行顺序,如在执行被依赖方法时,发现被依赖的方法未被执行,依赖方法会被忽略。

import pytest



@pytest.mark.dependency()
def test_b():
    pass

@pytest.mark.dependency(depends=["test_a"])
def test_c():
    pass

@pytest.mark.dependency(depends=["test_b"])
def test_d():
    pass

@pytest.mark.dependency(depends=["test_b", "test_c"])
def test_e():
    pass
@pytest.mark.dependency()
@pytest.mark.xfail(reason="deliberate fail")
def test_a():
    assert True
if __name__ == '__main__':
    pytest.main(["-sv"])


这个例子最后执行a,但c,e仍被忽略了。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值