pytest中参数化的几种方法
- @pytest.mark.parametrize
- 元组组成的列表进行参数化,每一组元素表示一组参数化值
- 每个参数单独赋值
- 添加用例id
- pytest.param, 针对单个参数化添加mark标记或者id
- 利用indirect参数,通过fixture实现间接参数化
- pytest_generate_tests
- 根据输入命令动态选择参数值
- 执行命令动态输入参数值
@pytest.mark.parametrize
pytest.mark.parametrize可以实现参数化,它包含的参数有:
- argnames:一个或者多各参数名,逗号分割
- argvalues:和argnames对应,若argname只有一个值列表,那么argvalues也只有一个值,如果有n个argname,argvalues必须时n元组的列表
- indirect:它的值是bool值或者argnames的子集;如果是True,则表示argnames中的所有名称,通过argname对应的fixture函数传递进行赋值,而不是在进行用例收集时就赋值
- ids:每组参数的id
- scope:指定参数范围
元组组成的列表进行参数化,每一组元素表示一组参数化值
- 如下使用testdata值进行参数化
#content of test_param.py
#coding=utf-8
import pytest
testdata = [
(3, 4, -1),
(6,3, 3),
]
@pytest.mark.parametrize("a,b,expected", testdata)
def test_diff_v0(a, b, expected):
diff = a - b
assert diff == expected
- 执行
pytest test_param.py --collect-only
查看收集的用例如下:
收集到两个用例test_diff_v0[3-4--1]
和test_diff_v0[6-3-3]
D:\Python\program\practic_unittest>pytest test_param.py --collect-only
============================================================================ test session starts =============================================================================
platform win32 -- Python 3.8.4, pytest-6.2.4, py-1.9.0, pluggy-0.13.1
rootdir: D:\Python\program\practic_unittest, configfile: pytest.ini
plugins: allure-pytest-2.8.16
collected 2 items
<Module test_param.py>
<Function test_diff_v0[3-4--1]>
<Function test_diff_v0[6-3-3]>
========================================================================= 2 tests collected in 0.02s =========================================================================
每个参数单独赋值
- 根据每个元素值的个数,每个参数值组合生成一条用例
#content of test_param.py
#coding=utf-8
import pytest
testdata = [
(3, 4),
(6,3),
]
@pytest.mark.parametrize("expected", [-1,3])
@pytest.mark.parametrize("a,b", testdata)
def test_diff_v1(a, b, expected):
diff = a - b
assert diff == expected
- 执行
pytest test_param.py --collect-only
查看收集的用例如下:
收集到四(2*2)个用例test_diff_v1[3-4--1]
、test_diff_v1[3-4-3]
、test_diff_v1[6-3--1]
和test_diff_v1[6-3-3]
D:\Python\program\practic_unittest>pytest test_param.py --collect-only
============================================================================ test session starts =============================================================================
platform win32 -- Python 3.8.4, pytest-6.2.4, py-1.9.0, pluggy-0.13.1
rootdir: D:\Python\program\practic_unittest, configfile: pytest.ini
plugins: allure-pytest-2.8.16
collected 4 items
<Module test_param.py>
<Function test_diff_v1[3-4--1]>
<Function test_diff_v1[3-4-3]>
<Function test_diff_v1[6-3--1]>
<Function test_diff_v1[6-3-3]>
========================================================================= 4 tests collected in 0.02s ========================================================================
添加用例id
- 通过以上的例子可以看出生成的用例名称后面跟的是参数的值,可以通过各
ids
或者id
赋值,指定用例标识
#content of test_param.py
# coding=utf-8
import pytest
testdata = [
(3, 4, -1),
(6, 3, 3),
]
@pytest.mark.parametrize("a,b,expected", testdata, ids=["test1", "test2"])
def test_diff_v2(a, b, expected):
diff = a - b
assert diff == expected
- 执行
pytest test_param.py --collect-only
查看收集的用例如下:
收集到两个用例test_diff_v2[test1]
和test_diff_v2[test2]
D:\Python\program\practic_unittest>pytest test_param.py --collect-only
============================================================================ test session starts =============================================================================
platform win32 -- Python 3.8.4, pytest-6.2.4, py-1.9.0, pluggy-0.13.1
rootdir: D:\Python\program\practic_unittest, configfile: pytest.ini
plugins: allure-pytest-2.8.16
collected 2 items
<Module test_param.py>
<Function test_diff_v2[test1]>
<Function test_diff_v2[test2]>
========================================================================= 2 tests collected in 0.02s =========================================================================
pytest.param, 针对单个参数化添加mark标记或者id
- 对于添加了mark标记的,可通过
-m name
来执行指定用例 -
pytest.param
的参数有:values
:参数值marks
: 参数添加mark标记,可以是单个值也可以是一个列表id
:设置参数的id
#content of test_param.py
# coding=utf-8
import pytest
#利用pytest.param添加
@pytest.mark.parametrize("a,b,expected",
[(4,2,1), pytest.param(9,3,6, marks=pytest.mark.xfail,id="expect fail")
,pytest.param(5,3,1,marks=[pytest.mark.first,pytest.mark.xfail],id="test3")],
)
def test_diff_v3(a, b, expected):
diff = a - b
assert diff == expected
#content of pytest.ini
[pytest]
markers=first:mark run first
- 执行
pytest test_param.py --collect-only
查看收集的用例如下:
收集到三个用例,但是只选择了有xfail
标记的test_diff_v3[expect fail]
和test_diff_v3[test3]
D:\Python\program\practic_unittest>pytest test_param.py --collect-only -m xfail
============================================================================ test session starts =============================================================================
platform win32 -- Python 3.8.4, pytest-6.2.4, py-1.9.0, pluggy-0.13.1
rootdir: D:\Python\program\practic_unittest, configfile: pytest.ini
plugins: allure-pytest-2.8.16
collected 3 items / 1 deselected / 2 selected
<Module test_param.py>
<Function test_diff_v3[expect fail]>
<Function test_diff_v3[test3]>
================================================================ 2/3 tests collected (1 deselected) in 0.02s =================================================================
利用indirect参数,通过fixture实现间接参数化
# content of test_param.py
# coding=utf-8
import pytest
@pytest.fixture(scope="function")
def x(request):
return request.param * 3
@pytest.fixture(scope="function")
def y(request):
return request.param * 2
@pytest.mark.parametrize("x, y", [("a", "b")], indirect=True)
def test_indirect(x, y):
assert x == "aaa"
assert y == "bb"
- 执行
pytest test_param.py
用例执行通过
D:\Python\program\practic_unittest>pytest test_param.py
============================================================================ test session starts =============================================================================
platform win32 -- Python 3.8.4, pytest-6.2.4, py-1.9.0, pluggy-0.13.1
rootdir: D:\Python\program\practic_unittest, configfile: pytest.ini
plugins: allure-pytest-2.8.16
collected 1 item
test_param.py::test_indirect[a-b] PASSED [100%]
============================================================================= 1 passed in 0.02s ==============================================================================
- 修改
test_param.py
中的@pytest.mark.parametrize("x, y", [("a", "b")], indirect=True)
为@pytest.mark.parametrize("x, y", [("a", "b")], indirect=["x"])
后,执行结果是不通过,因为在断言assert y == "bb"
时不通过
D:\Python\program\practic_unittest>pytest test_param.py
============================================================================ test session starts =============================================================================
platform win32 -- Python 3.8.4, pytest-6.2.4, py-1.9.0, pluggy-0.13.1
rootdir: D:\Python\program\practic_unittest, configfile: pytest.ini
plugins: allure-pytest-2.8.16
collected 1 item
test_param.py::test_indirect[a-b] FAILED [100%]
================================================================================== FAILURES ==================================================================================
_____________________________________________________________________________ test_indirect[a-b] _____________________________________________________________________________
x = 'aaa', y = 'b'
@pytest.mark.parametrize("x, y", [("a", "b")], indirect=["x"])
def test_indirect(x, y):
assert x == "aaa"
> assert y == "bb"
E AssertionError: assert 'b' == 'bb'
E - bb
E + b
test_param.py:16: AssertionError
========================================================================== short test summary info ===========================================================================
FAILED test_param.py::test_indirect[a-b] - AssertionError: assert 'b' == 'bb'
============================================================================= 1 failed in 0.13s ==============================================================================
pytest_generate_tests
- 利用
pytest_generate_tests
钩子可以动态确定参数或者fixture范围,通过传入的metafunc
对象可以检查请求上下文,调用metafunc.parametrize()
进行参数化 -
metafunc.parametrize()
的参数值和pytest.mark.parametrize
一样
根据输入命令动态选择参数值
比如需要在多个环境运行脚本,而每个环境的参数不一样,可通过一下方式来实现动态赋值
在conftest.py中定义
# content of test_param.py
# coding=utf-8
import pytest
def test_compute(param):
assert param < 4
#conftest.py
def pytest_addoption(parser):
parser.addoption("--dev", action="store_true", help="run in dev env,otherwise run in test env") #带--dev参数时默认值时true,不带时为false
# action的值store、store_const,store_true,store_false,append,append_const,count,extend
def pytest_generate_tests(metafunc):
if "param" in metafunc.fixturenames:
if metafunc.config.getoption("dev"):
name = [1,2,3]
else:
name = [3,4,5]
metafunc.parametrize("param", name)
- 执行
pytest test_param.py --collect-only
和执行pytest test_param.py --collect-only --dev
查看收集的用例如下:
分别取name=[3,4,5]
和name=[1,2,3]
的值
D:\Python\program\practic_unittest>pytest test_param.py --collect-only
============================================================================ test session starts =============================================================================
platform win32 -- Python 3.8.4, pytest-6.2.4, py-1.9.0, pluggy-0.13.1
rootdir: D:\Python\program\practic_unittest, configfile: pytest.ini
plugins: allure-pytest-2.8.16
collected 3 items
<Module test_param.py>
<Function test_compute[3]>
<Function test_compute[4]>
<Function test_compute[5]>
========================================================================= 3 tests collected in 0.02s =========================================================================
D:\Python\program\practic_unittest>pytest test_param.py --collect-only --dev
============================================================================ test session starts =============================================================================
platform win32 -- Python 3.8.4, pytest-6.2.4, py-1.9.0, pluggy-0.13.1
rootdir: D:\Python\program\practic_unittest, configfile: pytest.ini
plugins: allure-pytest-2.8.16
collected 3 items
<Module test_param.py>
<Function test_compute[1]>
<Function test_compute[2]>
<Function test_compute[3]>
========================================================================= 3 tests collected in 0.02s =========================================================================
执行命令动态输入参数值
如下代码parser.addoption
的action改为append
后(更多action值地址),在执行命令时把输入的值传给参数
# content of test_param.py
# coding=utf-8
import pytest
def test_compute(param):
assert param < 4
#conftest.py
def pytest_addoption(parser):
parser.addoption("--dev", action="apend", help="run in dev env,otherwise run in test env") #带--dev参数时默认值时true,不带时为false
#action的值store、store_const,store_true,store_false,append,append_const,count,extend
def pytest_generate_tests(metafunc):
if "param" in metafunc.fixturenames:
metafunc.parametrize("param",metafunc.config.getoption("dev")) #metafunc.config.getoption检索命令行选项的值,取到的dev值赋给param参数,metafunc.config.getini(name) 检索 ini 样式文件中读取的值
- 执行
pytest test_param.py --collect-only --dev 3
或者pytest test_param.py --collect-only --dev 3 --dev 45
分别收集到一条/两条用例:test_compute[3]
和test_compute[45]
D:\Python\program\practic_unittest>pytest test_param.py --collect-only --dev 3
============================================================================ test session starts =============================================================================
platform win32 -- Python 3.8.4, pytest-6.2.4, py-1.9.0, pluggy-0.13.1
rootdir: D:\Python\program\practic_unittest, configfile: pytest.ini
plugins: allure-pytest-2.8.16
collected 1 item
<Module test_param.py>
<Function test_compute[3]>
========================================================================= 1 test collected in 0.02s ==========================================================================
D:\Python\program\practic_unittest>pytest test_param.py --collect-only --dev 3 --dev 45
============================================================================ test session starts =============================================================================
platform win32 -- Python 3.8.4, pytest-6.2.4, py-1.9.0, pluggy-0.13.1
rootdir: D:\Python\program\practic_unittest, configfile: pytest.ini
plugins: allure-pytest-2.8.16
collected 2 items
<Module test_param.py>
<Function test_compute[3]>
<Function test_compute[45]>
========================================================================= 2 tests collected in 0.03s =========================================================================