前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Pytest学习笔记3——参数化

Pytest学习笔记3——参数化

作者头像
全栈测试开发日记
发布2023-02-02 17:33:01
6140
发布2023-02-02 17:33:01
举报
文章被收录于专栏:全栈测试开发日记

  前言

  在讲pytest与unittest的区别文章中,我们知道其中一个区别就是参数化,unittest框架使用的第三方库ddt来参数化的,而pytest框架就直接使用装饰器@pytest.mark.parametrize来对测试用例进行传参。这个是针对测试方法来参数化,还有一种是针对前置处理函数来传参。但往往这两种是可以结合使用。

  Params参数化(单个)

  前面讲fixture函数时,它有个参数params用来传递参数,并且与request结合使用,先看单个:

代码语言:javascript
复制
import pytest

seq = [1, 2, 3]


@pytest.fixture(params=seq)
def test_data(request):
    print("参数")
    return request.param


class TestData:
    def test_1(self, test_data):
        print("用例", test_data)


if __name__ == '__main__':
    pytest.main()

  运行结果:

代码语言:javascript
复制
fixture_test03.py::TestData::test_1[1] 参数
PASSED                            [ 33%]用例 1

fixture_test03.py::TestData::test_1[2] 参数
PASSED                            [ 66%]用例 2

fixture_test03.py::TestData::test_1[3] 参数
PASSED                            [100%]用例 3


============================== 3 passed in 0.05s ==============================

  Params参数化(多个)

  列表里有多个dict数据,request会自动化循环读取每个索引下的值,从0开始,看下面例子:

代码语言:javascript
复制
import pytest

seq = [{'a':1,'b':2},{'c':3,'d':4}]


@pytest.fixture(params=seq)
def test_data(request):
    print("参数:%s"%request.param)
    return request.param


class TestData:
    def test_1(self, test_data):
        print("用例", test_data)




if __name__ == '__main__':
    pytest.main()

  运行结果:

代码语言:javascript
复制
============================== 2 passed in 0.04s ==============================

Process finished with exit code 0
参数:{'a': 1, 'b': 2}
PASSED                   [ 50%]用例 {'a': 1, 'b': 2}
参数:{'c': 3, 'd': 4}
PASSED                   [100%]用例 {'c': 3, 'd': 4}

注意:params需要与request一起使用。

  装饰器@pytest.mark.parametrize参数化(单个)

  我们做接口测试时,编写测试用例需要输入一组数据,然后执行程序,得到输出数据,经过断言将实际结果与预期结果进行比较,从而得到这条用例执行的结果。但是输入的数据肯定不是一两个,这个时候需要参数化,比如登录的接口,我们可以用测试账号1登录,也可用账号2,账号3...进行登录,如果每一个参数写一条测试用例肯定是不行的,所以引入@pytest.mark.parametrize()装饰器进行参数化,这就是背景。

  格式:@pytest.mark.parametrize("参数名称","参数列表"),第一个是参数的名称,第二个是参数列表(list),也就是参数的值。

  如:@pytest.mark.parametrize("x",1, 2, 3)

  如:@pytest.mark.parametrize('请求方式,接口地址,传参,预期结果',('get','www.baidu.com','{"page":1}','{"code":0,"msg":"成功"})',('post','www.baidu.com','{"page":2}','{"code":0,"msg":"成功"}'))

  1、第一个参数是字符串,多个参数中间用逗号隔开

  2、第二个参数是list,多组数据用元祖类型;传三个或更多参数也是这样传。list的每个元素都是一个元组,元组里的每个元素和按参数顺序一一对应

  3、传一个参数 @pytest.mark.parametrize('参数名',list) 进行参数化

  4、传两个参数@pytest.mark.parametrize('参数名1,参数名2',[(参数1_data0, 参数2_data0),(参数1_data1, 参数2_data1)]) 进行参数化

代码语言:javascript
复制
seq = [1, 2, 3]

def get_data(x):
    print(x)
    pass


@pytest.mark.parametrize("x",seq)
def test_data(x):
    a = get_data(x)
    print(a)

if __name__ == '__main__':
    pytest.main()

  运行结果:

代码语言:javascript
复制
============================== 3 passed in 0.04s ==============================

Process finished with exit code 0
PASSED                                   [ 33%]1
None
PASSED                                   [ 66%]2
None
PASSED                                   [100%]3
None

  备注:第一个函数输出的是参数值,类中的函数打印的是外面那个函数的返回值,因为没有return,默认返回是None。

  与request结合使用

  如果装饰器@pytest.mark.parametrize与request结合使用,如果测试方法写在类中,则@pytest.mark.parametrize的参数名称要与@pytest.fixture函数名称保持一致。并且要添加indirect=True参数,目的是把ss_data当做函数去执行,而不是参数。

  如:

代码语言:javascript
复制
import pytest
seq = [1,2,3]
@pytest.fixture()
def ss_data(request):
    print("参数:%s"%request.param)
    return request.param



class TestData:
    @pytest.mark.parametrize("ss_data",seq,indirect=True)
    def test_1(self,ss_data):
        print("用例", ss_data)

if __name__ == '__main__':
    pytest.main()

  运行结果:

代码语言:javascript
复制
collecting ... collected 3 items

fixture_test03.py::TestData::test_1[1] 
fixture_test03.py::TestData::test_1[2] 
fixture_test03.py::TestData::test_1[3] 

============================== 3 passed in 0.04s ==============================

Process finished with exit code 0
参数:1
PASSED                            [ 33%]用例 1
参数:2
PASSED                            [ 66%]用例 2
参数:3
PASSED                            [100%]用例 3

  注意1:如果将@pytest.mark.parametrize参数名称改成x的执行,就会报错如:

  注意2:如果不使用fixture装饰器函数,也不使用外部函数,也是可以的,例子如下:

代码语言:javascript
复制
import pytest
seq = [1,2,3]

class TestData:
    @pytest.mark.parametrize("x",seq,indirect=False)
    def test_1(self,x):
        print("用例", x)

if __name__ == '__main__':
    pytest.main()

  运行结果:

代码语言:javascript
复制
============================== 3 passed in 0.04s ==============================

Process finished with exit code 0
PASSED                            [ 33%]用例 1
PASSED                            [ 66%]用例 2
PASSED                            [100%]用例 3

  可以看出,装饰器@pytest.mark.parametrize参数传递的是参数名称了,而不是函数了。根据开关indirect自由控制,默认是False.

  装饰器@pytest.mark.parametrize参数化(多个)

   多个参数和单个参数写法差不多,只不过多个参数中间用逗号隔开,列表中用元组来分组,举个例子(不带类的测试方法和没有使用request参数):

代码语言:javascript
复制
import pytest
seq = [(1,2),(3,4)]

def get_data(x,y):
    print(x)
    print(y)
    pass


@pytest.mark.parametrize("x,y",seq)
def test_data(x,y):
    get_data(x,y)
    # print(a)

if __name__ == '__main__':
    pytest.main()

  运行结果:

代码语言:javascript
复制
============================== 2 passed in 0.04s ==============================

Process finished with exit code 0
PASSED                                 [ 50%]1
2
PASSED                                 [100%]3
4

  这就是把参数列表list里面的元组中两个值,看作一对参数(x,y)。

  与request结合使用

  前面介绍过单个使用的场景,现在是多个参数的时候,如何使用呢?

  其实差不多,但是需要注意一些问题,先看个例子:

代码语言:javascript
复制
import pytest
seq = [(1,2),(3,4)]

@pytest.fixture()
def get_data(request):
    x = request.param[0]
    y = request.param[1]
    print(x,111)
    print(y,222)
    # print(request.param)
    pass

# indirect=True声明x是个函数
@pytest.mark.parametrize("get_data",seq)
def test_data(get_data):
    print(get_data)

if __name__ == '__main__':
    pytest.main()

  运行结果:

代码语言:javascript
复制
plugins: allure-pytest-2.8.6, rerunfailures-5.0
collecting ... collected 2 items

fixture_005.py::test_data[get_data0] PASSED                              [ 50%](1, 2)

fixture_005.py::test_data[get_data1] PASSED                              [100%](3, 4)


============================== 2 passed in 0.02s ==============================

Process finished with exit code 0

  我们可以看出来fixture装饰器函数根本没有执行,这是为什么呢?

  因为使用@pytest.mark.parametrize装饰器参数化时,默认是以参数而不是函数,这里的@pytest.mark.parametrize("get_data",seq)中get_data是参数名称,不是函数,如果要声明为函数,需要添加:indirect=True。

  我们将其改之,再运行:

代码语言:javascript
复制
import pytest
seq = [(1,2),(3,4)]

@pytest.fixture()
def get_data(request):
    x = request.param[0]
    y = request.param[1]
    print(x,111)
    print(y,222)
    # print(request.param)
    pass

# indirect=True声明x是个函数
@pytest.mark.parametrize("get_data",seq,indirect=True)
def test_data(get_data):
    print(get_data)

if __name__ == '__main__':
    pytest.main()

  运行结果:

代码语言:javascript
复制
fixture_005.py::test_data[get_data0] 1 111
2 222
PASSED                              [ 50%]None

fixture_005.py::test_data[get_data1] 3 111
4 222
PASSED                              [100%]None


============================== 2 passed in 0.02s ==============================

Process finished with exit code 0

  多个fixture与@pytest.mark.parametrize组合

  用例前面可以有多个条件,参数化装饰器也可以叠加,使用parametrize装饰器叠加时,用例组合是2个参数个数相乘

代码语言:javascript
复制
import pytest

seq1 = [1,2,3]
seq2 = [4,5,6]

@pytest.fixture()
def get_seq1(request):
    seq1 = request.param
    print("seq1:",seq1)
    return seq1

@pytest.fixture()
def get_seq2(request):
    seq2 = request.param
    print("seq2:", seq2)
    return seq2

@pytest.mark.parametrize("get_seq1",seq1,indirect=True)
@pytest.mark.parametrize("get_seq2",seq2,indirect=True)
def test_1(get_seq1,get_seq2):
    print(get_seq1,11)
    print(get_seq2,22)

if __name__ == '__main__':
    pytest.main()

  运行结果:

代码语言:javascript
复制
collecting ... collected 9 items

fixture_test04.py::test_1[4-1] 
fixture_test04.py::test_1[4-2] 
fixture_test04.py::test_1[4-3] 
fixture_test04.py::test_1[5-1] 
fixture_test04.py::test_1[5-2] 
fixture_test04.py::test_1[5-3] 
fixture_test04.py::test_1[6-1] 
fixture_test04.py::test_1[6-2] 
fixture_test04.py::test_1[6-3] 

============================== 9 passed in 0.07s ==============================

Process finished with exit code 0
seq1: 1
seq2: 4
PASSED                                    [ 11%]1 11
4 22
seq1: 2
seq2: 4
PASSED                                    [ 22%]2 11
4 22
seq1: 3
seq2: 4
PASSED                                    [ 33%]3 11
4 22
seq1: 1
seq2: 5
PASSED                                    [ 44%]1 11
5 22
seq1: 2
seq2: 5
PASSED                                    [ 55%]2 11
5 22
seq1: 3
seq2: 5
PASSED                                    [ 66%]3 11
5 22
seq1: 1
seq2: 6
PASSED                                    [ 77%]1 11
6 22
seq1: 2
seq2: 6
PASSED                                    [ 88%]2 11
6 22
seq1: 3
seq2: 6
PASSED                                    [100%]3 11
6 22

  如果使用类,也是一样的,如图:

   seq1和seq2参数都是3个,所以总的用例数就是9个。

  单独使用@pytest.mark.parametrize参数组合

  总结

  pytest与unittest的区别之一参数化已经讲完,希望可以帮助你学习pytest框架。

  如果对你有帮助或喜欢自动化测试开发的朋友,可以加入右下方QQ交流群学习与探索,更多干货与你分享。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-05-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  •   前言
  •   Params参数化(单个)
  •   Params参数化(多个)
  •   装饰器@pytest.mark.parametrize参数化(单个)
    •   与request结合使用
    •   装饰器@pytest.mark.parametrize参数化(多个)
      •   与request结合使用
      •   多个fixture与@pytest.mark.parametrize组合
        •   单独使用@pytest.mark.parametrize参数组合
        •   总结
        相关产品与服务
        访问管理
        访问管理(Cloud Access Management,CAM)可以帮助您安全、便捷地管理对腾讯云服务和资源的访问。您可以使用CAM创建子用户、用户组和角色,并通过策略控制其访问范围。CAM支持用户和角色SSO能力,您可以根据具体管理场景针对性设置企业内用户和腾讯云的互通能力。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档