以下内容纯属个人理解,如有不足,还请指正,转载请注明出处,喜欢请动动小指头点赞评论哦(▽)!
@pytest.mark.parametrize使用
前言
Pytest参数化有两种方式:
@pytest.fixture(params=[])
@pytest.mark.parametrize()
两者都会多次执行使用它的测试函数,但@pytest.mark.parametrize()使用方法更丰富一些
定义
@pytest.mark.parametrize(self,argnames, argvalues, indirect=False, ids=None, scope=None)):
参数:
参数 | 说明 |
---|---|
argnames | 必传,参数名, 以逗号分隔的字符串,表示一个或多个参数名称(key),或参数字符串的列表/元组 |
argvalues | 必传,参数值,若argnames有一个刚单值列表传入,若argnames有多个,以套用元组的列表展示,无组内与参数名一一对应 |
indirect | 为true时,那argnames一定是一个fixture函数名称,argvalues值将传入对应的fixture内,相当于@pytest.fixture(params=)的用法,默认False |
ids | 标记子用例执行名称,与argvalues数量一致,未指定自动生成,默认None |
scope | 如果指定,则表示参数的范围。范围用于按参数实例对测试进行分组。它还将覆盖任何fixture函数定义的范围,允许使用测试上下文或配置设置动态范围 |
一、 参数使用举栗
1、argnames、argvalues
I. 单参数单值
'''
@Author : 测试工程师Jane
@FileName : parametrizetest.py
@Description:
'''
import pytest
@pytest.mark.parametrize('arg',[1])
#测试函数要将argnames做为形参传入
def test_one_params(arg):
print("传入的值为:{}".format(arg))
assert arg == 1
II. 单参数多值
1.单参数多值,argvalues可以传入多样的python数据类型:列表,嵌套了元组的列表,字典,字符串
2.传入多个值时,测试用例会被执行多次,每次取一个值去运行
'''
@Author : 测试工程师Jane
@FileName : parametrizetest.py
@Description:
'''
import pytest
@pytest.mark.parametrize('arg',['abc',1,{'a':1,'b':3},(4,5)]
def test_one_params(arg):
print("传入的值为:{}".format(arg))
assert isinstance(arg,dict)
运行结果
从以上运行结果可以看出,当传入多个值时,测试用例会被执行多次,每次取一个值去运行,并断言。
III. 多参数多值
'''
@Author : 测试工程师Jane
@FileName : parametrizetest.py
@Description:
'''
import pytest
@pytest.mark.parametrize("test_input,expected",[("3+5",8),("5-2",1),("5*2",10)])
def test_params(test_input,expected):
print("原值:{} 期望值{}".format(test_input,expected))
assert eval(test_input) == expected
运行结果:
2、indirect ***
- indirect一般与Pytest的request、fixture组合使用
- 当indrect 为True时,argnames则要传入fixture函数名称,不再是一个普通参数,而是要被调用的fixture函数,argvalues则是要给这个函数传的值
- 作法其实与@pytest.fixture(params)一样,但使用了@pytest.mark.parametrize相当于参数化了fixture,而不是只有固定的一套数据传入使用
让我们来看下最简单的例子:
I、单fixture单值(通过列表)
'''
@Author : 测试工程师Jane
@FileName : parametrizetest.py
@Description:
'''
import pytest
@pytest.fixture()
def login(request):
user = request.param
print("传入的用户名为:{}".format(user))
return user
user = ['张三','李四']
@pytest.mark.parametrize('login',user,indirect=True)
def test_one_param(login):
print("测试类的读到的用户是:{}".format(login))
运行结果:
让我们来分析下这个小例子:
整个的调用的过程如下(仅供参考,不完全等同于源码处理逻辑):
II、单fixture多值(通过字典)
'''
@Author : 测试工程师Jane
@FileName : paratwo.py
@Description:
'''
import pytest
userinfo = [
{'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',userinfo,indirect=True)
def test_one_param(login):
print("测试类的读到的用户是:{} 密码是:{}".format(login['user'],login['pwd']))
运行结果:
III、传多fixture多值(通过嵌套元组的列表)
'''
@Time : 2021/1/5 17:14
@Author : 测试工程师Jane
@FileName : paratwo.py
@Description:
'''
import pytest
#一个@pytest.mark.parametrize使用多个fixture,传入的数据要是嵌套了元组的列表
userinfo = [
('张三',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',userinfo,indirect=True)
def test_one_param(login_user,login_pwd):
print("测试类的读到的用户是:{} 密码是:{}".format(login_user,login_pwd))
运行结果:
IV、叠加fixture(单值列表,执行次数笛卡尔集 N*M)
'''
@Author : 测试工程师Jane
@FileName : parathree.py
@Description:
'''
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))
运行结果:
从以上运行结果可以看出,测试用例一共执行了四次,也就是两个fixture传入值的积(笛卡尔集)
总结:
indirect=True的用法与False用法基本是类似的,唯一的区别是,当它和True的时候会将参数传入fixture再进行一次前置处理,将处理后的返回值再给测试函数使用。False是直接使用。
3、ids:
作用:标记子用例执行名称,增加可读性,中文需要转码:https://www.cnblogs.com/creamk87/p/13505605.htm
未加IDS之前执行结果:
加了IDS之后:
4、scope:
- scope的作用范围取值与fixture scope一致,当indirect=True才会被使用
- scope的作用范围会覆盖fixture的scope范围,如果同一个被调用的fixture有多个parametrize定义了scope,取第一条的范围,见下例:
先上代码,下面几条用的都是这套,只不用有小改动,不重复贴了:
'''
@Time : 2021/1/5 17:14
@Author : 测试工程师Jane
@FileName : paratwo.py
@Description:
'''
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))
I、小于Fixture的scope范围:
运行结果:
II、大于Fixture的scope范围:
III、多个SCOPE范围为先执行的:
我们调整下顺序
二、 与其它mark一起使用,标记子用例
举例:
'''
@Author : 测试工程师Jane
@FileName : marktest.py
@Description:
'''
import pytest
a = '跳过'
@pytest.mark.parametrize("x,y",
[('test1',123456),
pytest.param("test1",123456,marks=pytest.mark.xfail),
pytest.param("test1",1256,marks=pytest.mark.xfail),
pytest.param("test2",123,marks=pytest.mark.skip("跳过这条用例")),
pytest.param("test3",456,marks=pytest.mark.skipif(a =='跳过',reason="有条件的跳过"))
]
)
def test_one(x,y):
assert x== 'test1'
assert y == 123456
运行结果: