语法糖:
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)
运行命令:pytest test_parametrize.py -vs
collected 3 items
test_parametrize.py::test_one[1] 1 PASSED
test_parametrize.py::test_one[2] 2 PASSED
test_parametrize.py::test_one[3] 3 PASSED
说明:
argnames参数名只有一个参数的时候,使用参数名的字符串,argvalues参数值使用一个列表。
测试用例test_one使用装饰器parametrize,pytest每次运行测试用例的时候,参数名a都会从参数值[1,2,3]中取出一个值来运行子用例。
修改 test_parametrize.py 文件如下:
import pytest
# 多个参数名
@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
collected 3 items
test_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
说明:
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
collected 3 items
test_parametrize.py::test_three[1] 2 PASSED
test_parametrize.py::test_three[2] 3 PASSED
test_parametrize.py::test_three[3] 4 PASSED
说明:
当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
collected 6 items
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
说明:
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
collected 6 items
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
说明:
使用全局变量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
collected 3 items
test_parametrize.py::test_eval[3+5-8] PASSED
test_parametrize.py::test_eval[2+4-6] PASSED
test_parametrize.py::test_eval[6*9-42] XFAIL
说明:
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
collected 6 items
test_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
说明:
堆叠多个parametrize装饰器,在这种情况下,每次调用都会参数化所有先前的参数化。
reference:
API Reference — pytest documentation
How to parametrize fixtures and test functions — pytest documentation