11、Pytest之@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 ***
  1. indirect一般与Pytest的request、fixture组合使用
  2. 当indrect 为True时,argnames则要传入fixture函数名称,不再是一个普通参数,而是要被调用的fixture函数,argvalues则是要给这个函数传的值
  3. 作法其实与@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:
  1. scope的作用范围取值与fixture scope一致,当indirect=True才会被使用
  2. 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

运行结果:
在这里插入图片描述

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值