Pytest参数化你不知道的一些使用技巧 /你不知道的pytest
2024-03-29 15:06:43

Pytest参数化你不知道的一些使用技巧 /你不知道的pytest

前言

unittest单元测试框架使用DDT进行数据驱动测试,参数那么身为功能更加强大且更加灵活的知道知道Pytest框架怎么可能没有数据驱动的概念呢?其实Pytest是使用@pytest.mark.parametrize装饰器来实现数据驱动测试的,那么今天我们就简单来说说在它是使的如何进行数据驱动测试的 。

Pytest参数化你不知道的一些使用技巧 /你不知道的pytest

装饰测试类

"""nimport pytestnndata_1 = [n (1, 2, 3),n (4, 5, 9)n]nnndef add(a, b):n return a + bnnn@pytest.mark.parametrize('a, b, expect', data_1)nclass TestParametrize(object):nn def test_parametrize_1(self, a, b, expect):n print('n测试函数1测试数据为n{ }-{ }'.format(a, b))n assert add(a, b) == expectnn def test_parametrize_2(self, a, b, expect):n print('n测试函数2数据为n{ }-{ }'.format(a, b))n assert add(a, b) == expectnnnif __name__ == '__main__':n pytest.main(['-sv'])

输出

collecting ... collected 4 itemsnntest_parametrize.py::TestParametrize::test_parametrize_1[1-2-3] n测试函数1测试数据为n1-2nPASSEDntest_parametrize.py::TestParametrize::test_parametrize_1[4-5-9] n测试函数1测试数据为n4-5nPASSEDntest_parametrize.py::TestParametrize::test_parametrize_2[1-2-3] n测试函数2数据为n1-2nPASSEDntest_parametrize.py::TestParametrize::test_parametrize_2[4-5-9] n测试函数2数据为n4-5nPASSEDnn========================== 4 passed in 0.21 seconds ===========================nnProcess finished with exit code 0

说明

当装饰器装饰测试类时,给数据集合会被传递给给类的参数所有方法

装饰测试函数

单个数据

import pytestnndata = [1, 2]nnn@pytest.mark.parametrize('a', data)ndef test_parametrize(a):n print('n被加载测试数据为n{ }'.format(a))nnnif __name__ == '__main__':n pytest.main(['-s'])

输出

============================= test session starts =============================nplatform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0nrootdir: E:CnblogCodepytest_parametrize, inifile:nplugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0ncollected 2 itemsnntest_parametrize.py n被加载测试数据为n1n.n被加载测试数据为n2n.nn========================== 2 passed in 0.16 seconds ===========================nnProcess finished with exit code 0

说明

当测试用例只需要一个参数时,我们存放数据的知道知道列表无序嵌套序列 ,@pytest.mark.parametrize('a',使的 data)装饰器的第一个参数也只需要一个变量接收列表中的每个元素 ,第二个参数传递存储数据的用技列表 ,那么测试用例需要使用同名的参数字符串接收测试数据(实例中的a)且列表有多少个元素就会生成并执行多少个测试用例

一组数据

import pytestnndata = [n [1, 2, 3],n [4, 5, 9]n] # 列表嵌套列表n# data_tuple = [n# (1, 2, 3),n# (4, 5, 9)n# ] # 列表嵌套元组nnn@pytest.mark.parametrize('a, b, expect', data)ndef test_parametrize_1(a, b, expect): # 一个参数接收一个数据n print('n测试数据为n{ },{ },知道知道{ }'.format(a,使的 b, expect))n actual = a + bn assert actual == expectnnn@pytest.mark.parametrize('value', data)ndef test_parametrize_2(value): # 一个参数接收一组数据n print('n测试数据为n{ }'.format(value))n actual = value[0] + value[1]n assert actual == value[2]nnnif __name__ == '__main__':n pytest.main(['-s'])

输出

============================= test session starts =============================nplatform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0nrootdir: E:CnblogCodepytest_parametrize, inifile:nplugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0ncollected 4 itemsnntest_parametrize.py n测试数据为n1 ,2 ,用技3n.n测试数据为n4,参数5,知道知道9n.n测试数据为n[1,使的 2, 3]n.n测试数据为n[4, 5, 9]n.nn========================== 4 passed in 0.17 seconds ===========================nnProcess finished with exit code 0

说明

当测试用例需要多个数据时 ,我们可以使用嵌套序列(嵌套元组&嵌套列表)的列表来存放测试数据

装饰器@pytest.mark.parametrize()可以使用单个变量接收数据 ,也可以使用多个变量接收 ,同样,测试用例函数也需要与其保持一致

当使用单个变量接收时,测试数据传递到测试函数内部时为列表中的每一个元素或者小列表,需要使用索引的方式取得每个数据

当使用多个变量接收数据时,那么每个变量分别接收小列表或元组中的每个元素

列表嵌套多少个多组小列表或元组 ,测生成多少条测试用例

图解对应关系

Pytest参数化你不知道的一些使用技巧 /你不知道的pytest

组合数据

"""nimport pytestnndata_1 = [1, 2] ndata_2 = [3, 4] nnn@pytest.mark.parametrize('a', data_1)n@pytest.mark.parametrize('b', data_2)ndef test_parametrize_1(a, b):n print('n测试数据为n{ } ,{ }'.format(a, b))nnnif __name__ == '__main__':n pytest.main(['-s'])

输出

============================= test session starts =============================nplatform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0nrootdir: E:CnblogCodepytest_parametrize, inifile:nplugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0ncollected 4 itemsnntest_parametrize.py n测试数据为n1,3n.n测试数据为n2,3n.n测试数据为n1,4n.n测试数据为n2 ,4n.nn========================== 4 passed in 0.24 seconds ===========================nnProcess finished with exit code 0

说明

通过测试结果 ,我们不难分析 ,一个测试函数还可以同时被多个参数化装饰器装饰 ,那么多个装饰器中的数据会进行交叉组合的方式传递给测试函数 ,进而生成n*n个测试用例  ,这也为我们的测试设计时提供了方便

标记用例

可以直接标记测试用例,参数化装饰器也可以识别(标记用例失败或跳过)

标记为无条件跳过(标记为失败为xfail ,自己尝试)

"""nimport pytestnndata_1 = [n [1, 2, 3],n pytest.param(3, 4, 8, marks=pytest.mark.skip)n]nnndef add(a, b):n return a + bnnn@pytest.mark.parametrize('a, b, expect', data_1)ndef test_parametrize_1(a, b, expect):n print('n测试数据为n{ },{ }'.format(a, b))n assert add(a, b) == expectnnnif __name__ == '__main__':n pytest.main(['-vs'])

输出

============================= test session starts =============================nplatform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0 -- C:ProgramsPythonPython37-32python.exencachedir: .pytest_cachenmetadata: { 'Python': '3.7.2', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': { 'pytest': '4.3.1', 'py': '1.8.0', 'pluggy': '0.9.0'}, 'Plugins': { 'rerunfailures': '7.0', 'metadata': '1.8.0', 'html': '1.20.0'}, 'JAVA_HOME': 'D:JDK'}nrootdir: E:CnblogCodepytest_parametrize, inifile:nplugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0ncollecting ... collected 2 itemsnntest_parametrize.py::test_parametrize_1[1-2-3] n测试数据为n1  ,2nPASSEDntest_parametrize.py::test_parametrize_1[3-4-8] SKIPPEDnn===================== 1 passed, 1 skipped in 0.17 seconds =====================nnProcess finished with exit code 0

说明

输出结果显示收集到2个用例,一个通过 ,一个被跳过 ,当我们不想执行某组测试数据时,我们可以标记skip或skipif;当我们预期某组数据会执行失败时,我们可以标记为xfail等

嵌套字典

"""nimport pytestnndata_1 = (n { n 'user': 1,n 'pwd': 2n },n { n 'user': 3,n 'pwd': 4n }n)nnn@pytest.mark.parametrize('dic', data_1)ndef test_parametrize_1(dic):n print('n测试数据为n{ }'.format(dic))nnnif __name__ == '__main__':n pytest.main(['-s'])

输出

============================= test session starts =============================nplatform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0nrootdir: E:CnblogCodepytest_parametrize, inifile:nplugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0ncollected 2 itemsnntest_parametrize.py n测试数据为n{ 'user': 1, 'pwd': 2}n.n测试数据为n{ 'user': 3, 'pwd': 4}n.nn========================== 2 passed in 0.20 seconds ===========================nnProcess finished with exit code 0

增加可读性

使用ids参数

参数化装饰器有一个额外的参数ids,可以标识每一个测试用例,自定义测试数据结果的显示 ,为了增加可读性 ,我们可以标记每一个测试用例使用的测试数据是什么 ,适当的增加一些说明

在使用前你需要知道,ids参数应该是一个字符串列表 ,必须和数据对象列表的长度保持一致,我们可以试着使用ids,看下效果

"""nimport pytestnndata_1 = [n (1, 2, 3),n (4, 5, 9)n]nnnids = ["a:{ } + b:{ } = expect:{ }".format(a, b, expect) for a, b, expect in data_1]nndef add(a, b):n return a + bnnn@pytest.mark.parametrize('a, b, expect', data_1, ids=ids) nclass TestParametrize(object):nn def test_parametrize_1(self, a, b, expect):n print('n测试函数1测试数据为n{ }-{ }'.format(a, b))n assert add(a, b) == expectnn def test_parametrize_2(self, a, b, expect):n print('n测试函数2数据为n{ }-{ }'.format(a, b))n assert add(a, b) == expectnnnif __name__ == '__main__':n pytest.main(['-v']) # -v  : 更加详细的输出测试结果

输出

装饰器不传递ids参数的输出

collecting ... collected 4 itemsnntest_parametrize.py::TestParametrize::test_parametrize_1[1-2-3] PASSED [ 25%]ntest_parametrize.py::TestParametrize::test_parametrize_1[4-5-9] PASSED [ 50%]ntest_parametrize.py::TestParametrize::test_parametrize_2[1-2-3] PASSED [ 75%]ntest_parametrize.py::TestParametrize::test_parametrize_2[4-5-9] PASSED [100%]nn========================== 4 passed in 0.16 seconds ===========================nnProcess finished with exit code 0

装饰器传递ids参数的输出

collecting ... collected 4 itemsnntest_parametrize.py::TestParametrize::test_parametrize_1[a:1 + b:2 = expect:3] PASSED [ 25%]ntest_parametrize.py::TestParametrize::test_parametrize_1[a:4 + b:5 = expect:9] PASSED [ 50%]ntest_parametrize.py::TestParametrize::test_parametrize_2[a:1 + b:2 = expect:3] PASSED [ 75%]ntest_parametrize.py::TestParametrize::test_parametrize_2[a:4 + b:5 = expect:9] PASSED [100%]nn========================== 4 passed in 0.20 seconds ===========================nnProcess finished with exit code 0

说明

执行命令我使用了-v,会更加详细的显示输出结果 ,可以看到所有的数据结果中的用例都被一个列表明确的标记了,而且通过这种标记可以更加直观的看出来,每个测试用例使用的数据名称及测试内容

自定义id做标识

除了使用ids参数增加输出可读性外,我们还可以在参数列表的参数旁边定义一个id值来做标识,看下面实例

"""nimport pytestnndata_1 = [n pytest.param(1, 2, 3, id="(a+b):pass"), # id的值可以自定义 , 只要方便理解每个用例是干什么的即可n pytest.param(4, 5, 10, id="(a+b):fail")n]nnndef add(a, b):n return a + bnnnclass TestParametrize(object):nn @pytest.mark.parametrize('a, b, expect', data_1)n def test_parametrize_1(self, a, b, expect):n assert add(a, b) == expectnnnif __name__ == '__main__':n pytest.main(['-v'])

输出

test_parametrize.py::TestParametrize::test_parametrize_1[(a+b):pass] PASSED [ 50%]ntest_parametrize.py::TestParametrize::test_parametrize_1[(a+b):fail] FAILED [100%]nn================================== FAILURES ===================================n_______________ TestParametrize.test_parametrize_1[(a+b):fail] ________________nnself = <pytest_parametrize.test_parametrize.TestParametrize object at 0x000001D7BFC4C748>na = 4, b = 5, expect = 10nn @pytest.mark.parametrize('a, b, expect', data_1)n def test_parametrize_1(self, a, b, expect):n> assert add(a, b) == expectnE assert 9 == 10nE -9nE +10nntest_parametrize.py:28: AssertionErrorn===================== 1 failed, 1 passed in 0.35 seconds ======================nnProcess finished with exit code 0

说明

如果使用此方法来标记测试用例,一定要严格按照我写的格式来使用 ,语法为pytest.param(value, id='somthing')

总结

Pytest中实现数据驱动就是如此了

掌握

1.装饰器与测试用例使用单个变量接收多组数据与多个变量接收多个数据的访问方法

2.不同测试数据形式(列表嵌套元组,列表,字典等)时,如何传递数据及访问数据

3.装饰器装饰测试类和测试函数的不同之处:装饰测试类时,类内所有的方法必须接送测试数据,否则会报错,装饰测试函数时比较灵活 ,如果函数不使用数据就可以不装饰

4.为了输出结果的可读性 ,可以选择使用ids参数 ,与测试数据中定义id参数值来标识测试用例

注意

1. 装饰器的第一个参数是一个列表形式的字符串参数"a, b, c" 不能写成"a", "b", "c"

2. ids是个字符串列表 ,它的长度需要与测试数据列表的长度一致

最后今天的文章就到这里了,喜欢的小伙伴可以点赞收藏评论加关注 。

Pytest参数化你不知道的一些使用技巧 /你不知道的pytest

(作者:产品中心)