@pytest.mark. parametrize装饰器可以实现对测试用例的参数化,方便测试数据的获取。
方便测试函数对测试数据的获取。
方法:
parametrize(argnames, argvalues, indirect=False, ids=None, scope=None)
# file_name: test_parametrize.py
import pytest
class Test_D:
@pytest.mark.parametrize("a", [1, 2, 3]) # 参数a被赋予3个值,test_a将会运行3遍
def test_a(self, a): # 参数必须和parametrize里面的参数一致
print('\n------------------> test_a has ran, and a = {}'.format(a))
assert 1 == a
if __name__ == '__main__':
pytest.main(['-s', 'test_parametrize.py'])
运行结果:
从运行结果中可以看到test_a方法被执行了3遍,说明参数a参数化成功。
# file_name:test_parametrize.py
import pytest
class Test_D:
@pytest.mark.parametrize("a,b", [(1, 2), (2, 3), (3, 4)]) # 参数a,b均被赋予三个值,函数会运行三遍
def test_b(self, a, b): # 参数必须和parametrize里面的参数一致
print('\n------------------> test_b has ran, and a = {}, b = {}'.format(a, b))
assert 1
if __name__ == '__main__':
pytest.main(['-s', 'test_parametrize.py'])
运行结果:
从运行结果中可以看到test_b方法被执行了三遍,说明参数a,b都已经被参数化。
# file_name: test_parametrize.py
import pytest
# 定义返回参数值的函数
def return_test_data():
return [(1, 2), (2, 3), (3, 4)]
class Test_D:
@pytest.mark.parametrize("a,b", return_test_data()) # 使用函数返回值的方式传入参数值
def test_c(self, a, b):
print('\n------------------> test_c has ran, and a = {}, b = {}'.format(a, b))
assert 1
if __name__ == '__main__':
pytest.main(['-s', 'test_parametrize.py'])
运行结果:
从运行结果中可以看到test_c方法被执行了三遍,这说明使用函数的返回值方式同样可以做到参数化。
获得多个参数化参数的所有组合,可以堆叠使用参数化装饰器:【每一个参数化装饰器代表参数化测试方法中的一组测试数据】
# file_name: test_parametrize.py
import pytest
class Test_D:
@pytest.mark.parametrize("x", [1, 2])
@pytest.mark.parametrize("y", [3, 4])
def test_d(self, x, y):
print("\n------------------> test_d has ran, and x={}, y={}".format(x, y))
if __name__ == '__main__':
pytest.main(['-s', 'test_parametrize.py'])
运行结果:
从运行结果中可以看到test_d方法被执行了四遍,这是因为参数x和参数y分别被赋予了2个值,组合起来就是2*2=4次,说明装饰器叠加可以获得所有参数的组合。
① indirect
参数一般与pytest中的fixture函数中的 request
组合使用。
②当 indrect=True
时,argnames则要传入fixture函数名称,不再是一个普通参数,而是要被调用的fixture函数,argvalues则是要给这个fixture函数传的值。
③用法其实与 @pytest.fixture(params)
一样,但使用了 @pytest.mark.parametrize
相当于参数化了fixture函数,而不是只有固定的一套数据传入使用。
import pytest
test_user_data = ['Tom', 'Jerry']
# 方法名作为参数
@pytest.fixture(scope='module')
def login_r(request):
user = request.param # 通过 request.param 获取测试参数
print(f" 登录用户: {user}")
return user
@pytest.mark.parametrize("login_r", test_user_data, indirect=True)
def test_login(login_r):
a = login_r
print(f"测试用例中 login_r 函数 的返回值; {a}")
assert a != ""
运行结果:
import pytest
@pytest.fixture()
def login(request):
user = request.param
print("传入的用户名为:{}".format(user))
return user
user = ['张三', '李四']
@pytest.mark.parametrize('login', user, indirect=True) # 此时测试函数参数化的值为login fixture函数的返回值(参数列表user中存在几组数据就进行了几次参数化)
def test_one_param(login):
print("测试函数的读到的用户是:{}".format(login))
运行结果:
详细解释:
整个调用过程如下:
import pytest
user_info = [
{'user': '张三', 'pwd': 123},
{'user': '李四', 'pwd': 456}
]
@pytest.fixture()
def login(request):
user = request.param
print("传入的用户名为:{},密码为:{}".format(user['user'], user['pwd']))
return user
@pytest.mark.parametrize('login', user_info, indirect=True)
def test_one_param(login):
print("测试类的读到的用户是:{} 密码是:{}".format(login['user'], login['pwd']))
运行结果:
import pytest
# 一个@pytest.mark.parametrize使用多个fixture,传入的数据要是嵌套了元组的列表
user_info = [
('张三', 123),
('李四', 'pwd')
]
@pytest.fixture()
def login_user(request):
user = request.param
print("传入的用户名为:{}".format(user))
return user
@pytest.fixture()
def login_pwd(request):
pwd = request.param
print("密码为:{}".format(pwd))
return pwd
@pytest.mark.parametrize('login_user,login_pwd', user_info, indirect=True)
def test_one_param(login_user, login_pwd):
print("测试类的读到的用户是:{} 密码是:{}".format(login_user, login_pwd))
运行结果:
import pytest
user = ['张三', '李四']
pwd = [124, 345]
@pytest.fixture()
def login_user(request):
user = request.param
print("传入的用户名为:{}".format(user))
return user
@pytest.fixture()
def login_pwd(request):
pwd = request.param
print("密码为:{}".format(pwd))
return pwd
@pytest.mark.parametrize('login_pwd', pwd, indirect=True)
@pytest.mark.parametrize('login_user', user, indirect=True)
def test_one_param(login_user, login_pwd):
print("测试类的读到的用户是:{} 密码是:{}".format(login_user, login_pwd))
运行结果:
① scope
参数的作用范围取值与fixture函数的scope一致,且hi有当 indirect=True
才会被使用。
② scope
参数的作用范围会覆盖fixture函数的scope范围,如果同一个被调用的fixture函数有多个parametrize定义了scope,取第一条的范围。
import pytest
@pytest.fixture(scope='class')
def login_user(request):
user = request.param
print("传入的用户名为:{}".format(user))
return user
@pytest.fixture(scope='class')
def login_pwd(request):
pwd = request.param
print("密码为:{}".format(pwd))
return pwd
class TestCase:
userinfo = [
('张三', 123)
]
ids = ["case{}".format(i) for i in range(len(userinfo))]
@pytest.mark.parametrize('login_user,login_pwd', userinfo, ids=ids, indirect=True, scope='function')
def test_one_param(self, login_user, login_pwd):
print("测试类的读到的内容是{}{}".format(login_user, login_pwd))
@pytest.mark.parametrize('login_user,login_pwd', userinfo, ids=ids, indirect=True, scope='function')
def test_one_param2(self, login_user, login_pwd):
print("测试类的读到的内容是{}{}".format(login_user, login_pwd))
运行结果:
①小于fixture函数的scope范围:
②大于fixture函数的scope范围:
③多个SCOPE范围为先执行的:
与③相比调整一下顺序:
需求:pytest 使用 @pytest.mark.parametrize
对测试用例进行参数化的时候,当存在多组测试数据时,需要对其中的一组或者多组测试数据加标记跳过执行,可以用 pytest.param 实现。
① param values :按顺序传参数集值的变量args
② keyword marks : marks关键字参数,要应用于此参数集的单个标记或标记列表。
③ keyword str id : id字符串关键字参数,测试用例的id属性【测试用例名】
def param(*values, **kw):
"""Specify a parameter in `pytest.mark.parametrize`_ calls or
:ref:`parametrized fixtures `.
.. code-block:: python
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
pytest.param("6*9", 42, marks=pytest.mark.xfail),
])
def test_eval(test_input, expected):
assert eval(test_input) == expected
:param values: variable args of the values of the parameter set, in order.
:keyword marks: a single mark or a list of marks to be applied to this parameter set.
:keyword str id: the id to attribute to this parameter set.
"""
return ParameterSet.param(*values, **kw)
import pytest
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
pytest.param("6*9", 42, marks=pytest.mark.xfail),
])
def test_eval(test_input, expected):
assert eval(test_input) == expected
运行结果:
import pytest
@pytest.mark.parametrize("user,psw",
[("student", "123456"),
("teacher", "abcdefg"),
pytest.param("admin", "我爱中华!", marks=pytest.mark.skip(reason='跳过原因'))
])
def test_login(user, psw):
print(user + " : " + psw)
assert 1 == 1
运行结果:
import pytest
@pytest.mark.parametrize("number",
[pytest.param("1"),
pytest.param("2"),
pytest.param("3", marks=pytest.mark.skip(reason='该参数未准备好'))])
def test_login1(number):
print(number)
assert 1 == 1
运行结果:
import pytest
@pytest.mark.parametrize("user,psw",
[pytest.param("admin1", "123456"),
pytest.param("admin2", "abcdefg"),
pytest.param("admin3", "higklmn", marks=pytest.mark.skip('此用户的登录信息还没有初始化,不可使用'))])
def test_login1(user, psw):
print(user + " : " + psw)
assert 1 == 1
运行结果:
①id参数是给用例添加标题内容,没加id参数的时候,用例会默认拿请求的参数当用例标题;如实例1
②id参数是指单条参数化测试用例数据时,分别为同一测试方法中的每一条测试用例进行命名;ids参数是指多条参数化测试用例数据时,ids参数传入一个列表或者元组,分别为同一测试方法中的所有测试用例进行命名。如实例2、实例3
import pytest
@pytest.mark.parametrize("user,psw",
[pytest.param("admin1", "abcdefg"),
pytest.param("admin2", "123456"),
pytest.param("admin3", "qwerty", marks=pytest.mark.skip)])
def test_login1(user, psw):
print(user + " : " + psw)
assert 1 == 1
运行结果:
import pytest
@pytest.mark.parametrize("user,psw",
[pytest.param("admin1", "abcdefg", id="第一条测试用例名字"),
pytest.param("admin2", "123456", id="第二条测试用例名字"),
pytest.param("admin3", "qwerty", marks=pytest.mark.skip, id="第三条测试用例名字")])
def test_login1(user, psw):
print(user + " : " + psw)
assert 1 == 1
运行结果:
import pytest
@pytest.mark.parametrize("user,psw", argvalues=[("admin1", "abcdefg"), ("admin2", "123456"), ("admin3", "qwerty")],
ids=["第一条测试用例名字", "第二条测试用例名字", "第三条测试用例名字"])
def test_login1(user, psw):
print(user + " : " + psw)
assert 1 == 1
运行结果:
自动化测试相关教程推荐:
2023最新自动化测试自学教程新手小白26天入门最详细教程,目前已有300多人通过学习这套教程入职大厂!!_哔哩哔哩_bilibili
2023最新合集Python自动化测试开发框架【全栈/实战/教程】合集精华,学完年薪40W+_哔哩哔哩_bilibili
测试开发相关教程推荐
2023全网最牛,字节测试开发大佬现场教学,从零开始教你成为年薪百万的测试开发工程师_哔哩哔哩_bilibili
postman/jmeter/fiddler测试工具类教程推荐
讲的最详细JMeter接口测试/接口自动化测试项目实战合集教程,学jmeter接口测试一套教程就够了!!_哔哩哔哩_bilibili
2023自学fiddler抓包,请一定要看完【如何1天学会fiddler抓包】的全网最详细视频教程!!_哔哩哔哩_bilibili
2023全网封神,B站讲的最详细的Postman接口测试实战教学,小白都能学会_哔哩哔哩_bilibili
如果对你有帮助的话,点个赞收个藏,给作者一个鼓励。也方便你下次能够快速查找。
如有不懂还要咨询下方小卡片,博主也希望和志同道合的测试人员一起学习进步
在适当的年龄,选择适当的岗位,尽量去发挥好自己的优势。
我的自动化测试开发之路,一路走来都离不每个阶段的计划,因为自己喜欢规划和总结,
测试开发视频教程、学习笔记领取传送门!!