当前位置: 首页 > news >正文

全网最牛自动化测试框架系列之pytest(8)-参数化

【文章末尾给大家留下了大量的福利】

前言

什么是参数化,通俗点理解就是,定义一个测试类或测试函数,可以传入不同测试用例对应的参数,从而执行多个测试用例。

例如对登录接口进行测试,假设有3条用例:正确账号正确密码登录、正确账号错误密码登录、错误账号正确密码登录,那么我们只需要定义一个登陆测试函数test_login(),然后使用这3条用例对应的参数去调用test_login()即可。

在unittest中可以使用ddt进行参数化,而pytest中也提供非常方便的参数化方式,即使用装饰器@pytest.mark.parametrize()

一般写为pytest.mark.parametrize("argnames", argvalues),其中:

  1. argnames为参数名称,可以是单个或多个,多个写法为"argname1, argname2, ......"
  2. argvalues为参数值,类型必须为list(单个参数时可以为元组,多个参数时必须为list,所以最好统一)

当然pytest.mark.parametrize()不止这两个参数,感兴趣的可以去查看源代码,后面我们还会讲到其中的参数ids

示例中所请求的登陆接口信息为:

  1. 接口url:http://127.0.0.1:5000/login
  2. 请求方式:post
  3. 请求参数:{"username": "xxx", "password": "xxxxxx"}
  4. 响应信息:{"code": 1000, "msg": "登录成功", "token": "xxxxxx"}

单个参数

只需要传入一个参数时,示例如下:

# 待测试函数
def sum(a):
    return a+1

# 单个参数
data = [1, 2, 3, 4]
@pytest.mark.parametrize("item", data)
def test_add(item):
    actual = sum(item)
    print("\n{}".format(actual))
    # assert actual == 3

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

注意,@pytest.mark.parametrize()中的第一个参数,必须以字符串的形式来标识测试函数的入参,如上述示例中,定义的测试函数test_login()中传入的参数名为item,那么@pytest.mark.parametrize()的第一个参数则为"item"

运行结果如下:

rootdir: E:\blog\python接口自动化\apiAutoTest, configfile: pytest.ini
plugins: html-2.1.1, metadata-1.10.0, ordering-0.6, rerunfailures-9.1.1
collecting ... collected 4 items

test_case_2.py::test_add[1] PASSED                                       [ 25%]
2

test_case_2.py::test_add[2] PASSED                                       [ 50%]
3

test_case_2.py::test_add[3] PASSED                                       [ 75%]
4

test_case_2.py::test_add[4] PASSED                                       [100%]
5


============================== 4 passed in 0.02s ==============================

从结果我们可以看到,测试函数分别传入了data中的参数,总共执行了5次。

多个参数

测试用例需传入多个参数时,@pytest.mark.parametrize()的第一个参数同样是字符串, 对应用例的多个参数用逗号分隔。

示例如下:

import pytest
import requests
import json

# 列表嵌套元组
data = [("lilei", "123456"), ("hanmeimei", "888888")]
# 列表嵌套列表
# data = [["lilei", "123456"], ["hanmeimei", "888888"]]

@pytest.mark.parametrize("username, password", data)
def test_login(username, password):
    headers = {"Content-Type": "application/json;charset=utf8"}
    url = "http://127.0.0.1:5000/login"
    _data = {
        "username": username,
        "password": password
    }
    res = requests.post(url=url, headers=headers, json=_data).text
    res = json.loads(res)
    assert res['code'] == 1000


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

这里需要注意:

  1. 代码中data的格式,可以是列表嵌套列表,也可以是列表嵌套元组,列表中的每个列表或元组代表一组独立的请求参数。
  2. "username, password"不能写成 "username", "password"。

运行结果如下:

从结果中我们还可以看到每次执行传入的参数,如下划线所示部分。

这里所举示例是2个参数,传入3个或更多参数时,写法也同样如此,一定要注意它们之间一一对应的关系,如下图:

对测试类参数化

上面所举示例都是对测试函数进行参数化,那么对测试类怎么进行参数化呢?

其实,对测试类的参数化,就是对测试类中的测试方法进行参数化。@pytest.mark.parametrize()中标识的参数个数,必须与类中的测试方法的参数一致。示例如下:

# 将登陆接口请求单独进行了封装,仅仅只是为了方便下面的示例
def login(username, password):
    headers = {"Content-Type": "application/json;charset=utf8"}
    url = "http://127.0.0.1:5000/login"
    _data = {
        "username": username,
        "password": password
    }
    res = requests.post(url=url, headers=headers, json=_data).text
    res = json.loads(res)
    return res

# 测试类参数化
data = [
    ("lilei", "123456"), ("hanmeimei", "888888")
]
@pytest.mark.parametrize("username, password", data)
class TestLogin:
    def test_login_01(self, username, password):
        res = login(username, password)
        assert res['code'] == 1000

    def test_login_02(self, username, password):
        res = login(username, password)
        assert res['msg'] == "登录成功!"

if __name__ == '__main__':
    pytest.main(["-s"])

运行结果如下:

从结果中可以看出来,总共执行了4次,测试类中的每个测试方法都执行了2次,即每个测试方法都将data中的每一组参数都执行了一次。

注意,这里还是要强调参数对应的关系,即@pytest.mark.parametrize()中的第一个参数,需要与测试类下面的测试方法的参数一一对应。

参数组合

在编写测试用例的过程中,有时候需要将参数组合进行接口请求,如示例的登录接口中username有 lilei、hanmeimei,password有 123456、888888,进行组合的话有下列四种情况:

{"username": "lilei", "password": "123456"}
{"username": "lilei", "password": "888888"}
{"username": "hanmeimei", "password": "123456"}
{"username": "hanmeimei", "password": "888888"}

@pytest.mark.parametrize()也提供了这样的参数组合功能,编写格式示例如下:

import pytest
import requests
import json

username = ["lilei", "hanmeimei"]
password = ["123456", "888888"]

@pytest.mark.parametrize("password", password)
@pytest.mark.parametrize("username", username)
def test_login(username, password):
    headers = {"Content-Type": "application/json;charset=utf8"}
    url = "http://127.0.0.1:5000/login"
    _data = {
        "username": username,
        "password": password
    }
    res = requests.post(url=url, headers=headers, json=_data).text
    res = json.loads(res)
    assert res['code'] == 1000
    
    
if __name__ == '__main__':
    pytest.main()

运行结果如下:

rootdir: E:\blog\python接口自动化\apiAutoTest, configfile: pytest.ini
plugins: html-2.1.1, metadata-1.10.0, ordering-0.6, rerunfailures-9.1.1
collecting ... collected 4 items

test_case_5.py::test_login[lilei-123456] PASSED                          [ 25%]
test_case_5.py::test_login[lilei-888888] FAILED                          [ 50%]
test_case_5.py::test_login[hanmeimei-123456] FAILED                      [ 75%]
test_case_5.py::test_login[hanmeimei-888888] PASSED                      [100%]
=========================== short test summary info ===========================
FAILED test_case_5.py::test_login[lilei-888888] - assert 1001 == 1000
FAILED test_case_5.py::test_login[hanmeimei-123456] - assert 1001 == 1000
========================= 2 failed, 2 passed in 0.18s =========================

从结果可以看出来,2个username、2个password 有4中组合方式,总执行了4次。如果是3个username、2个password,那么就有6中参数组合方式,依此类推。

注意,以上这些示例中的测试用例仅仅只是用于举例,实际项目中的登录接口测试脚本与测试数据会不一样。

增加测试结果可读性

从示例的运行结果中我们可以看到,为了区分参数化的运行结果,在结果中都会显示由参数组合而成的执行用例名称,很方便就能看出来执行了哪些参数组合的用例,如下示例:

但这只是简单的展示,如果参数多且复杂的话,仅仅这样展示是不够清晰的,需要添加一些说明才能一目了然。

因此,在@pytest.mark.parametrize()中有两种方式来自定义上图中划线部分的显示结果,即使用@pytest.mark.parametrize()提供的参数 ids 自定义,或者使用pytest.param()中的参数id自定义。

ids(推荐)

ids使用方法示例如下:

import pytest
import requests
import json

data = [("lilei", "123456"), ("hanmeimei", "888888")]
ids = ["username:{}-password:{}".format(username, password) for username, password in data]
@pytest.mark.parametrize("username, password", data, ids=ids)
def test_login(username, password):
    headers = {"Content-Type": "application/json;charset=utf8"}
    url = "http://127.0.0.1:5000/login"
    _data = {
        "username": username,
        "password": password
    }
    res = requests.post(url=url, headers=headers, json=_data).text
    res = json.loads(res)
    assert res['code'] == 1000
    

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

从编写方式可以看出来,ids就是一个list,且它的长度与参数组合的分组数量一致。

运行结果如下:

比较上面个执行结果,我们能看出ids自定义执行结果与默认执行结果展示的区别。使用过程中,需要根据实际情况来自定义。

id

使用方式示例如下:

import pytest
import requests
import json

data = [
    pytest.param("lilei", "123456", id="correct username and correct password"),
    pytest.param("lilei", "111111", id="correct user name and wrong password")
]

@pytest.mark.parametrize("username, password", data)
def test_login(username, password):
    headers = {"Content-Type": "application/json;charset=utf8"}
    url = "http://127.0.0.1:5000/login"
    _data = {
        "username": username,
        "password": password
    }
    res = requests.post(url=url, headers=headers, json=_data).text
    res = json.loads(res)
    assert res['code'] == 1000


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

运行结果如下:

从个人实际使用体验来说,感觉id相对于ids,自定义的扩展性相当较小,而且我尝试着在id中使用中文进行定义,控制台显示的不是中文,而是编码后的结果。所以推荐使用ids。当然也可能是它的作用我没理解透彻。

总结

以上功能基本能覆盖我们平常自动化测试项目中的绝大部分场景。

当然,pytest.mark.parametrize()进行参数化的过程中,还有一些别的功能,如结合pytest.param()标记用例失败或跳过,感兴趣的可以自行查找使用方式,这里不做过多说明。

 重点:学习资料学习当然离不开资料,这里当然也给你们准备了600G的学习资料

【需要的可以扫描文章末尾的qq群二维码自助拿走】

【记得(备注“csdn000”)】

【或私信000】

群里的免费资料都是笔者十多年测试生涯的精华。还有同行大神一起交流技术哦。

项目实战:

大型电商平台:

全套软件测试自动化测试教学视频

300G教程资料下载【视频教程+PPT+项目源码】

全套软件测试自动化测试大厂面经

python自动化测试++全套模板+性能测试

听说关注我并三连的铁汁都已经升职加薪暴富了哦!!!!

 

相关文章:

  • App Store上架iOS App的详细流程
  • 链动2+1模式系统,如何抓住酒业新局的机遇?
  • 携创教育:2022年自考大专需要什么条件?怎么报名?
  • 基于Cookie-Editor与curl实现跨设备的文件下载
  • 百度智慧交通组-EdgeBoard基础操作合集
  • 1.4_8 Axure RP 9 for mac 高保真原型图 - 案例7 【旋转的唱片5】进度条计时器 关联
  • 岑溪农产品检验实验室建设细节概述
  • tf.convert_to_tensor
  • 具有良好水溶性的122266-55-1,Sulfo-NHS-SS-Biotin
  • 分析Jetpack Compose动画内部是如何实现的
  • ZTG-事务码使用日志报表
  • 不服不行!一篇文章带你透彻了解华为认证,考它真的值吗?
  • AXI死锁
  • python笔记III--流程控制语句
  • Java如何提高代码效率
  • SegmentFault for Android 3.0 发布
  • Elasticsearch 参考指南(升级前重新索引)
  • hadoop集群管理系统搭建规划说明
  • Java 多线程编程之:notify 和 wait 用法
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • js写一个简单的选项卡
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 使用SAX解析XML
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 最近的计划
  • 大数据全解:定义、价值及挑战
  • 交换综合实验一
  • 如何通过报表单元格右键控制报表跳转到不同链接地址 ...
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (二)构建dubbo分布式平台-平台功能导图
  • (力扣记录)1448. 统计二叉树中好节点的数目
  • (收藏)Git和Repo扫盲——如何取得Android源代码
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • .bat批处理出现中文乱码的情况
  • .htaccess配置重写url引擎
  • .NET 3.0 Framework已经被添加到WindowUpdate
  • .NET Micro Framework初体验
  • .NET Remoting Basic(10)-创建不同宿主的客户端与服务器端
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • @angular/cli项目构建--Dynamic.Form
  • @font-face 用字体画图标
  • [ C++ ] STL---string类的模拟实现
  • [AIGC] Redis基础命令集详细介绍
  • [BZOJ5250][九省联考2018]秘密袭击(DP)
  • [C++] 多线程编程-thread::yield()-sleep_for()
  • [CISCN2021 Quals]upload(PNG-IDAT块嵌入马)
  • [CLickhouse] 学习小计
  • [CVPR 2023:3D Gaussian Splatting:实时的神经场渲染]
  • [echarts] y轴不显示0