语法糖:
parametrize
(argnames, argvalues, indirect=False, ids=None, scope=None, *, _param_mark=None)
argnames:参数名,以逗号分隔的字符串,表示一个或多个参数名称。或参数字符串组成的列表/元组。
如果是一个参数,使用参数名的字符串。
如果是多个参数,多个参数名之间使用逗号分隔的字符串,或者多个参数名组成的列表,或者多个参数名组成的元组。
argvalues:参数值,类型是一个可迭代对象,和参数名一一对应。
如果argnames为一个参数,则argvalues是一个值列表。
如果argnames为多个参数,则argvalues必须是一个嵌套元组的列表,其中每个元组元素值与参数名一一对应。
indirect:参数名称列表(参数名称的子集)或布尔值。
indirect一般与Pytest的fixture,request.param组合使用
当indrect =True时,argnames参数名是fixture夹具的函数名,argvalues则是给这个夹具函数传递的参数。
ids:标记参数化测试用例的执行名称,默认自动生成,多个参数名之间用"-"连接。
scope:参数范围。
新建 test_parametrize.py 文件如下:
import pytest # 单个参数名 @pytest.mark.parametrize('a', [1, 2, 3]) def test_one(a): print(a)
运行命令:ytest test_parametrize.py -vs
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>pytest test_parametrize.py -vs
==================================== test session starts ====================================
platform win32 -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- e:\programs\python\python38\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.3', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '3.1.1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'assume': '2.2.0', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\\Program Files\\Java\\jdk1.8.0_271'}
rootdir: C:\Users\057776\PycharmProjects\pytest-demo\testmark, configfile: pytest.ini
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, assume-2.2.0, requests-mock-1.7.0
collected 3 itemstest_parametrize.py::test_one[1] 1 PASSED
test_parametrize.py::test_one[2] 2 PASSED
test_parametrize.py::test_one[3] 3 PASSED==================================== 3 passed in 0.04s ====================================
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>
说明:
argnames参数名只有一个参数的时候,使用参数名的字符串,argvalues参数值使用一个列表。
测试用例test_one使用装饰器parametrize,pytest每次运行测试用例的时候,参数名a都会从参数值[1,2,3]中取出一个值来运行子用例。
修改 test_parametrize.py 文件如下:
import pytest # 单个参数名 @pytest.mark.parametrize('a', [1, 2, 3]) def test_one(a): print(a) # 多个参数名 @pytest.mark.parametrize("a,b", [('a1', 'b1'), ('a2', 'b2'), ('a3', 'b3')]) # @pytest.mark.parametrize(['a', 'b'], [('a1', 'b1'), ('a2', 'b2'), ('a3', 'b3')]) # @pytest.mark.parametrize(('a', 'b'), [('a1', 'b1'), ('a2', 'b2'), ('a3', 'b3')]) def test_two(a, b): print(a, b)
运行命令:pytest test_parametrize.py -vs
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>pytest test_parametrize.py -vs
=================================== test session starts ===================================
platform win32 -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- e:\programs\python\python38\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.3', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '3.1.1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'assume': '2.2.0', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\\Program Files\\Java\\jdk1.8.0_271'}
rootdir: C:\Users\057776\PycharmProjects\pytest-demo\testmark, configfile: pytest.ini
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, assume-2.2.0, requests-mock-1.7.0
collected 6 itemstest_parametrize.py::test_one[1] 1 PASSED
test_parametrize.py::test_one[2] 2 PASSED
test_parametrize.py::test_one[3] 3 PASSED
test_parametrize.py::test_two[a1-b1] a1 b1 PASSED
test_parametrize.py::test_two[a2-b2] a2 b2 PASSEDtest_parametrize.py::test_two[a3-b3] a3 b3 PASSED
=================================== 6 passed in 0.07s ===================================
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>
说明:
argnames参数名有多个的时候,多个参数名之间使用逗号分隔的字符串,多个参数名组成的列表,多个参数名组成的元组,这三种写法都可以。
argnames参数名有多个的时候,argvalues必须是一个嵌套元组的列表,其中每个元组元素值与参数名一一对应。
ids参数,默认自动生成,多个参数名之间用"-"连接。
pytest运行的时候,parametrize装饰的测试用例test_two依次从参数值列表[('a1', 'b1'), ('a2', 'b2'), ('a3', 'b3')]中取出3个元组,运行3次测试用例。
修改 test_parametrize.py 文件如下:
import pytest# 夹具参数化 @pytest.fixture() def fixture_demo(request): return request.param + 1 lst = [1, 2, 3] # indirect=True,argnames作为参数传入参数名对应的夹具函数 @pytest.mark.parametrize('fixture_demo', lst, indirect=True) def test_three(fixture_demo): print(fixture_demo)
运行命令:pytest test_parametrize.py -vs
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>pytest test_parametrize.py -vs
================================= test session starts =================================
platform win32 -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- e:\programs\python\python38\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.3', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '3.1.
1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'assume': '2.2.0', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\\Program Files\\Java\\jdk1.8.0_271'}
rootdir: C:\Users\057776\PycharmProjects\pytest-demo\testmark, configfile: pytest.ini
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, assume-2.2.0, requests-mock-1.7.0
collected 3 itemstest_parametrize.py::test_three[1] 2 PASSED
test_parametrize.py::test_three[2] 3 PASSED
test_parametrize.py::test_three[3] 4 PASSED=================================== 3 passed in 0.05s ===================================
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>
说明:
当indrect =True时,argnames参数名是fixture夹具的函数名,argvalues则是给这个夹具函数传递值的参数。
夹具pytest.fixture(params) 可以用来实现参数化,参数化的时候这里需要用到一个参数request,用来接收fixture返回的结果,通过request.param来返回参数内容。
修改 test_parametrize.py 文件如下:
import pytest # 装饰类需要注意参数要和类中的所有测试函数保持一致 @pytest.mark.parametrize("a,b", [('a1', 'b1'), ('a2', 'b2'), ('a3', 'b3')]) class TestClass: def test_four(self, a, b): print(a, b) def test_five(self, a, b): print(a + b)
运行命令:pytest test_parametrize.py -vs
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>pytest test_parametrize.py -vs
=================================== test session starts ===================================
platform win32 -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- e:\programs\python\python38\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.3', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '3.1.
1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'assume': '2.2.0', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\\Program Files\\Java\\jdk1.8.0_271'}
rootdir: C:\Users\057776\PycharmProjects\pytest-demo\testmark, configfile: pytest.ini
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, assume-2.2.0, requests-mock-1.7.0
collected 6 itemstest_parametrize.py::TestClass::test_four[a1-b1] a1 b1 PASSED
test_parametrize.py::TestClass::test_four[a2-b2] a2 b2 PASSED
test_parametrize.py::TestClass::test_four[a3-b3] a3 b3 PASSED
test_parametrize.py::TestClass::test_five[a1-b1] a1b1 PASSED
test_parametrize.py::TestClass::test_five[a2-b2] a2b2 PASSED
test_parametrize.py::TestClass::test_five[a3-b3] a3b3 PASSED=================================== 6 passed in 0.20s ===================================
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>
说明:
parametrize装饰的类TestClass中的所有测试用例都调用了参数化。
修改 test_parametrize.py 文件如下:
import pytest pytestmark = pytest.mark.parametrize("a,b", [('a1', 'b1'), ('a2', 'b2'), ('a3', 'b3')]) def test_two(a, b): print(a, b) class TestClass: def test_four(self, a, b): print(a, b) def test_five(self, a, b): print(a + b)
运行命令:pytest test_parametrize.py -vs
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>pytest test_parametrize.py -vs
=================================== test session starts ===================================
platform win32 -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- e:\programs\python\python38\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.3', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '3.1.1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'assume': '2.2.0', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\\Program Files\\Java\\jdk1.8.0_271'}
rootdir: C:\Users\057776\PycharmProjects\pytest-demo\testmark, configfile: pytest.ini
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, assume-2.2.0, requests-mock-1.7.0
collected 9 itemstest_parametrize.py::test_two[a1-b1] a1 b1 PASSED
test_parametrize.py::test_two[a2-b2] a2 b2 PASSED
test_parametrize.py::test_two[a3-b3] a3 b3 PASSED
test_parametrize.py::TestClass::test_four[a1-b1] a1 b1 PASSED
test_parametrize.py::TestClass::test_four[a2-b2] a2 b2 PASSED
test_parametrize.py::TestClass::test_four[a3-b3] a3 b3 PASSED
test_parametrize.py::TestClass::test_five[a1-b1] a1b1 PASSED
test_parametrize.py::TestClass::test_five[a2-b2] a2b2 PASSED
test_parametrize.py::TestClass::test_five[a3-b3] a3b3 PASSED==================================== 9 passed in 0.08s ====================================
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>
说明:
使用全局变量pytestmark = pytest.mark.parametrize(),可以参数化模块中的所有测试用例。
修改 test_parametrize.py 文件如下:
import pytest # 使用内置mark.xfail,在参数化中标记单个测试用例预期执行失败 @pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), pytest.param("6*9", 42, marks=pytest.mark.xfail)]) def test_eval(test_input, expected): assert eval(test_input) == expected
运行命令:pytest test_parametrize.py -vs
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>pytest test_parametrize.py -v
================================== test session starts ==================================
platform win32 -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- e:\programs\python\python38\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.3', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '3.1.1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'assume': '2.2.0', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\\Program Files\\Java\\jdk1.8.0_271'}
rootdir: C:\Users\057776\PycharmProjects\pytest-demo\testmark, configfile: pytest.ini
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, assume-2.2.0, requests-mock-1.7.0
collected 3 itemstest_parametrize.py::test_eval[3+5-8] PASSED [ 33%]
test_parametrize.py::test_eval[2+4-6] PASSED [ 66%]
test_parametrize.py::test_eval[6*9-42] XFAIL [100%]=============================== 2 passed, 1 xfailed in 0.46s ===============================
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>
说明:
xfail 标记的测试用例预期执行失败,实际执行失败,则状态标记为XFAIL。
修改 test_parametrize.py 文件如下:
import pytest # 堆叠多个parametrize装饰器,在这种情况下,每次调用都会参数化所有先前的参数化。 @pytest.mark.parametrize('a', ['a1', 'a2']) @pytest.mark.parametrize('b', ['b1', 'b2', 'b3']) def test_over(a, b): print(a, b)
运行命令:pytest test_parametrize.py -vs
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>pytest test_parametrize.py -vs
=================================== test session starts ===================================
platform win32 -- Python 3.8.8, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 -- e:\programs\python\python38\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.8.8', 'Platform': 'Windows-10-10.0.19041-SP0', 'Packages': {'pytest': '6.2.3', 'py': '1.10.0', 'pluggy': '0.13.1'}, 'Plugins': {'html': '3.1.1', 'metadata': '1.11.0', 'rerunfailures': '9.1.1', 'assume': '2.2.0', 'requests-mock': '1.7.0'}, 'JAVA_HOME': 'C:\\Program Files\\Java\\jdk1.8.0_271'}
rootdir: C:\Users\057776\PycharmProjects\pytest-demo\testmark, configfile: pytest.ini
plugins: html-3.1.1, metadata-1.11.0, rerunfailures-9.1.1, assume-2.2.0, requests-mock-1.7.0
collected 6 itemstest_parametrize.py::test_over[b1-a1] a1 b1 PASSED
test_parametrize.py::test_over[b1-a2] a2 b1 PASSED
test_parametrize.py::test_over[b2-a1] a1 b2 PASSED
test_parametrize.py::test_over[b2-a2] a2 b2 PASSED
test_parametrize.py::test_over[b3-a1] a1 b3 PASSED
test_parametrize.py::test_over[b3-a2] a2 b3 PASSED=================================== 6 passed in 0.07s ===================================
(venv) C:\Users\057776\PycharmProjects\pytest-demo\testmark>
说明:
堆叠多个parametrize装饰器,在这种情况下,每次调用都会参数化所有先前的参数化。
待整理
reference:
API Reference — pytest documentation
How to parametrize fixtures and test functions — pytest documentation