pytest教程-8-用例参数化方法

领取资料,咨询答疑,请➕wei:  June__Go

上一小节中我们学习了pytest用例前后置方法的使用,本小节我们讲解一下pytest用例的参数化方法。

参数化简介:

参数化测试是指在测试用例中通过传入不同的参数来运行多次测试,以验证被测函数或方法的不同输入输出。其实也就是数据驱动测试的概念。在 unittest 中,使用ddt库配合unittest实现数据驱动。在pytest中并不需要额外的库,通过pytest.mark.parametrize()即可实现参数化。总之,pytest参数化使得我们可以方便地对测试用例进行扩展,减少了冗余代码,提高了测试的效率。

pytest有以下几种传参方式:

  • @pytest.mark.parametrize() 通过装饰器方式进行参数化(最常使用)
  • pytest.fixture()方式进行参数化,fixture装饰的函数可以作为参数传入其他函数
  • conftest.py文件中存放参数化函数,可作用于模块内的所有测试用例
  • 使用pytest的钩子函数pytest_generate_tests来自定义参数化测试的生成方式
  • 使用pytest-data库:pytest-data是一个用于参数化测试的扩展库,可以通过加载外部数据文件来提供参数化测试的数据
  • 使用自定义参数化装饰器:除了pytest提供的装饰器,还可以自定义参数化装饰器来实现特定的参数化方式

使用parametrize进行参数化

@pytest.mark.parametrize() 装饰器接收两个参数,第一个参数是以字符串的形式标识用例函数的参数,第二个参数以列表或元组的形式传递测试数据。

@pytest.mark.parametrize('参数化名称',参数化值) 该方法可以作用测试类和测试函数中

1、单个参数(data数据既可以是列表也可以是元组)

data为单一列表

import pytest
data=["lucy","lisa"]

@pytest.mark.parametrize('name',data)
def test_params(name):
    print(f'测试数据是{name}')

data为单一元组

import pytest
data=("lucy","lisa")

@pytest.mark.parametrize('name',data)
def test_params(name):
    print(f'测试数据是{name}')

data为列表里嵌套列表

import pytest
 
data = [
    [1,1,2],
    [2,2,4],
    [3,3,6],
    [4,4,8]
]
@pytest.mark.parametrize("test_data",data)
def test_login(test_data):
    print(f"username is {test_data}")

data为列表里嵌套元组

import pytest
 
data = [
    (1,1,2),
    (2,2,4),
    (3,3,6),
    (4,4,8)
]
@pytest.mark.parametrize("test_data",data)
def test_login(test_data):
    print(f"username is {test_data}")

data为列表里嵌套字典

import pytest
 
data = [{"user": "admin", "password": "123456"},
         {"user": "super", "password": "654321"},
         {"user": "sysadmin", "password": "321456"}
         ]
@pytest.mark.parametrize("test_data",data)
def test_login(test_data):
    print(f"username is {test_data['user']}\n password is {test_data['password']}")

2、多个参数

data为列表里嵌套列表

import pytest
 
data = [
    [1,1,2],
    [2,2,4],
    [3,3,6],
    [4,4,8]
]
@pytest.mark.parametrize("a,b,c",data)
def test_login(a,b,c):
    print(f"\na,b,c is: {a},{b},{c}")

data为列表里嵌套元组

import pytest

data = [
    (1, 1, 2),
    (2, 2, 4),
    (3, 3, 6),
    (4, 4, 8)
]


@pytest.mark.parametrize("a,b,c", data)
def test_login(a, b, c):
    print(f"\na,b,c is: {a},{b},{c}")

3、多个parametrize参数叠加(结果种类为多个参数相乘)

在实际测试中,有的场景多条件查询,比如登录有2个条件,名字有两种密码有四种情况,如果要全部覆盖,则是2*4==8种情况。这种情景,人工测试一般是不会全部覆盖的,但在自动化测试中,只要你想,就可以做到。如下示例:

import pytest

class TestAdd():
    @pytest.mark.parametrize('pwd', [33,None,44, 55])
    @pytest.mark.parametrize('name', [11, 22,])
    def test_add1(self, name, pwd):
        print(f'name:{name} pwd:{pwd}')

运行结果:2*4==8种情况

collected 8 items                                                                                                                                                                                                                                                                                                                                                   

test_demo.py::TestAdd::test_add1[11-33] PASSED                                                                                                                                                                                                                                                                                                                 [1/8]
test_demo.py::TestAdd::test_add1[11-None] PASSED                                                                                                                                                                                                                                                                                                               [2/8]
test_demo.py::TestAdd::test_add1[11-44] PASSED                                                                                                                                                                                                                                                                                                                 [3/8]
test_demo.py::TestAdd::test_add1[11-55] PASSED                                                                                                                                                                                                                                                                                                                 [4/8]
test_demo.py::TestAdd::test_add1[22-33] PASSED                                                                                                                                                                                                                                                                                                                 [5/8]
test_demo.py::TestAdd::test_add1[22-None] PASSED                                                                                                                                                                                                                                                                                                               [6/8]
test_demo.py::TestAdd::test_add1[22-44] PASSED                                                                                                                                                                                                                                                                                                                 [7/8]
test_demo.py::TestAdd::test_add1[22-55] PASSED      

4.1、ids 自定义测试id

通过上面的运行结果,我们可以看到,为了区分参数化的运行结果,在结果中都会显示数据组合而成的名称。

测试结果会自动生成测试id,自动生成的id短小还好说,如果数据比较长而复杂的话,那么就会很难看。

@pytest.mark.parametrize() 提供了 ids 参数来自定义显示结果,就是为了好看易读。

import pytest

class TestAdd():
    @pytest.mark.parametrize('name,pwd', [(10,11),(20,21),(30,31)],ids=(['zhangsan','lisi','wangmazi']))
    def test_add1(self, name, pwd):
        print(f'name:{name} pwd:{pwd}')

运行结果:以zhangsan、zhangsan、wangmazi显示

collected 3 items                                                                                                                                                                                                                                                                                                                                                   

test_demo.py::TestAdd::test_add1[zhangsan] PASSED                                                                                                                                                                                                                                                                                                              [1/3]
test_demo.py::TestAdd::test_add1[zhangsan] PASSED                                                                                                                                                                                                                                                                                                                  [2/3]
test_demo.py::TestAdd::test_add1[wangmazi] PASSED 

4.2 pytest.param 自定义测试id

在参数化测试中,每个测试用例可能包含多组参数,并且可能会产生大量的测试结果。这时,为了更好地理解和调试测试结果,给每个参数化测试用例指定一个易于理解的标识是很有意义的。而 pytest.param 函数的 id 参数就能做到这一点。

import pytest


@pytest.mark.parametrize("input,expected", [pytest.param(2, 4, id="case1"),
                                            pytest.param(3, 9, id="case2"),
                                            pytest.param(5, 25, id="case3")])
def test_multiply(input, expected):
    assert input * input == expected

运行结果:以case1、case2、case3显示

============================= test session starts =============================
collecting ... collected 3 items

test_demo.py::test_multiply[case1] PASSED                                [ 33%]
test_demo.py::test_multiply[case2] PASSED                                [ 66%]
test_demo.py::test_multiply[case3] PASSED                                [100%]

============================== 3 passed in 0.03s ==============================

4.3 pytest.param自定义选项

除了测试id参数,pytest.param 函数还可以接受额外的参数,例如 marks 参数,用于为单个测试用例应用自定义标记。通过使用 marks 参数,我们可以在参数化测试中灵活地添加各种自定义选项。

import pytest


@pytest.mark.parametrize("input,expected", [pytest.param(2, 4, id="case1"),
                                            pytest.param(3, 10, id="case2", marks=pytest.mark.xfail),
                                            pytest.param(5, 25, id="case3", marks=pytest.mark.skip)])
def test_multiply(input, expected):
    assert input * input == expected

运行结果:


test_demo.py::test_multiply[case1] 
test_demo.py::test_multiply[case2] 
test_demo.py::test_multiply[case3] 

=================== 1 passed, 1 skipped, 1 xfailed in 0.25s ===================
PASSED                                [ 33%]XFAIL                                 [ 66%]
input = 3, expected = 10

    @pytest.mark.parametrize("input,expected", [pytest.param(2, 4, id="case1"),
                                                pytest.param(3, 10, id="case2", marks=pytest.mark.xfail),
                                                pytest.param(5, 25, id="case3",marks=pytest.mark.skip)])
    
    def test_multiply(input, expected):
>       assert input * input == expected
E       assert (3 * 3) == 10

在上述示例中,我们使用 pytest.mark.skip, pytest.mark.xfail标记这两个测试用例。通过这种方式,我们可以对不同的参数化测试用例应用不同的标记,以实现更加灵活的测试控制 。

5、使用CSV或Excel文件作为测试数据进行参数化

可以使用Python的csv或openpyxl库来读取文件并生成参数组合。以下是一个示例:

import pytest
import csv

def read_csv(file_path):
    with open(file_path, 'r') as csv_file:
        csv_reader = csv.DictReader(csv_file)
        for row in csv_reader:
            yield row

@pytest.mark.parametrize("data", read_csv("test_data.csv"))
def test_multiply(data):
    num1 = int(data['num1'])
    num2 = int(data['num2'])
    expected = int(data['expected'])
    assert num1 * num2 == expected

使用pytest.fixture进行参数化

上面介绍了pytest中的自带的参数化方法,我们也可以通过使用fixture中的params参数来做参数化。

request.param:用于获取测试的请求参数。【获取测试上下文的信息】

①【注意】fixture函数的 params 请求参数数量(请求参数的数据类型为列表/元组,请求参数数量为列表/元组元素个数)决定fixture函数执行的次数。

②【注意】此时fixture函数的装饰器 @pytest.fixture(params=get_data) 参数不能忘记传值。

1、单个参数

import pytest
data3 = [{"user": "admin", "password": "123456"},
         {"user": "super", "password": "654321"},
         {"user": "sysadmin", "password": "321456"}
         ]
@pytest.fixture(params=data3,autouse=True,scope="class")
def get_data(request):
    print("fixture begin")
    yield request.param
    print("fixture end")
 
class TestLogin:
    def test_login(self,get_data):
        test_data = get_data
        print("username is {} AND password is {}".format(test_data["user"], test_data["password"]))

2、多个参数

import pytest
#此处是列表嵌套元祖
data1 = [('admin', '12346'), ("super", "654321"), ("sysadmin", '321456')]
data2 = [('admin', '12346'), ("super", "654321"), ("sysadmin", '321456')]

@pytest.fixture(params=data1,autouse=True,scope="class")
def get_data_1(request):
    print("fixture begin")
    yield request.param
    print("fixture end")

@pytest.fixture(params=data2,autouse=True,scope="class")
def get_data_2(request):
    print("fixture begin")
    yield request.param
    print("fixture end")
class TestLogin:
    def test_login(self,get_data_1,get_data_2):
        test_data_1 = get_data_1
        test_data_2 = get_data_2
        print("username is {};password is {}".format(get_data_1, get_data_2))

3、pytest.fixture与parametrize结合一起使用

fixture自身的params参数可以结合request来传参,当然也可以用parametrize来参数化代替params

如果测试方法写在类中,则@pytest.mark.parametrize的参数名称要与@pytest.fixture函数名称保持一致

单个参数

import pytest

seq = [1, 2, 3]

@pytest.fixture()
def ss_data(request):
    print("\n参数 %s" % request.param)
    return request.param + 1

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

多个fixture和多个parametrize叠加

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)

使用conftest.py进行参数化

conftest.py特点:

  • conftest.py 文件中存放参数化函数,可作用于模块内的所有测试用例
  • conftest.py 配置里可以实现数据共享,不需要import就能自动找到一些配置,pytest默认读取里面的配置

conftest.py配置需要注意以下点:

  • conftest.py配置脚本名称是固定的,不能改名称
  • conftest.py与运行的用例要在同一个pakage下,并且有__init__.py文件
  • 不需要import导入 conftest.py,pytest用例会自动查找

conftest.py应用场景

1、每个接口需共用到的token

conftest.py

import pytest
 
@pytest.fixture(scope='session')
def get_token():
    token = 'qeehfjejwjwjej11sss112'
    return token

test_params.py

def test_get_token(get_token):
    token = get_token
    print(token)

2、每个接口需共用到的测试用例数据

编写一个fixture在conftest.py,内容如下

data = [["admin","123456"],["superadmin","654321"]]
@pytest.fixture(scope='session',params=data)
def get_test_data(request):
    yield request.param

在测试脚本里面使用,只需要引入即可,新建一个测试文件test_params.py,内容如下

def test_get_data(get_test_data):
    print(f"user is {get_test_data[0]} and pwd is {get_test_data[1]}")

3、每个接口需共用到的配置信息

编写一个fixture在conftest.py,内容如下

@pytest.fixture(scope='session')
def get_base_url():
    base_url = "http://www.baidu.com"
    return base_url

在测试脚本里面使用,只需要引入即可,新建一个测试文件test_params.py,内容如下

def test_base_url(get_base_url):
    url = get_base_url
    print(url)

使用pytest_generate_tests钩子函数参数化

可以使用pytest的钩子函数pytest_generate_tests来自定义参数化测试的生成方式。下面是一个示例:

import pytest

def pytest_generate_tests(metafunc):
    if 'num' in metafunc.fixturenames:
        metafunc.parametrize('num', [1, 2, 3])

def test_square(num):
    assert num ** 2 == num * num

在上面的示例中,定义了一个pytest_generate_tests钩子函数,通过判断测试函数的参数是否存在来进行参数化。每个参数组合都会作为单独的测试用例执行。

使用pytest-data库参数化

pytest-data是一个用于参数化测试的扩展库,可以通过加载外部数据文件来提供参数化测试的数据。以下是一个示例:

import pytest
from pytest_data import data

@pytest.mark.datafiles('test_data.csv')
def test_addition(datafiles):
    data_file = datafiles / 'test_data.csv'
    for row in data(data_file):
        num1 = row['num1']
        num2 = row['num2']
        expected = row['expected']
        assert num1 + num2 == expected

在上面的示例中,使用pytest-data库的data装饰器加载了一个CSV文件作为测试数据,并在测试函数中使用了这些数据进行参数化测试。

使用自定义参数化装饰器进行参数化

除了pytest提供的装饰器,还可以自定义参数化装饰器来实现特定的参数化方式。示例如下

import pytest

def custom_parametrize(*args):
    def decorator(func):
        for arg in args:
            func = pytest.mark.parametrize(*arg)(func)
        return func
    return decorator

@custom_parametrize(
    ("num", [1, 2, 3]),
    ("operation", ["add", "subtract"])
)
def test_calculator(num, operation):
    if operation == "add":
        result = num + num
        assert result == 2 * num
    elif operation == "subtract":
        result = num - num
        assert result == 0

在上面的示例中,定义了一个自定义的参数化装饰器custom_parametrize,接受一系列参数化参数,并将其应用于测试函数。使用自定义装饰器可以实现更复杂的参数化逻辑。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走,希望可以帮助到大家!领取资料,咨询答疑,请➕wei:  June__Go

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/356606.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

图像复原的天花板在哪里?SUPIR:开创性结合文本引导先验和模型规模扩大

SUPIR(Scaling-UP Image Restoration),这是一种开创性的图像复原方法,利用生成先验和模型扩大规模的力量。通过利用多模态技术和先进的生成先验,SUPIR在智能和逼真的图像复原方面取得了重大进展。作为SUPIR中的关键催化…

纵向拼接,一键高效,让图片处理更简单!

你是否曾经因为需要批量处理图片而感到烦恼?现在,有了我们的图片处理工具,你可以轻松地纵向拼接图片,一键批量处理,让图片处理工作更加高效!这款工具采用先进的技术,能够快速准确地完成图片纵向…

Android SystemUI 介绍

目录 一、什么是SystemUI 二、SystemUI应用源码 三、学习 SystemUI 的核心组件 四、修改状态与导航栏测试 本篇文章,主要科普的是Android SystemUI , 下一篇文章我们将介绍如何把Android SystemUI 应用转成Android Studio 工程项目。 一、什么是Syst…

大数据 - Spark系列《一》- 分区 partition数目设置详解

目录 🐶3.2.1 分区过程 🐶3.2.2 SplitSize计算和分区个数计算 🐶3.2.3 Partition的数目设置 1. 🥙对于数据读入阶段,输入文件被划分为多少个InputSplit就会需要多少初始task. 2. 🥙对于转换算子产生的…

在centos 7 中安装配置Jdk、Tomcat、及Tomcat自启动

目录 一、安装配置Jdk 1.创建目录并上传文件 2.解压JDK压缩包 3.配置JDK环境变量 4.设置环境变量生效 二、安装配置Tomcat 1.上传Tomcat并解压 2.启停Tomcat 3.修改tomcat-user.xml配置 4.配置远程访问Tomcat 5.远程项目发布 三.Tomcat自启动配置 1.配置Tomcat自启…

imx6ull学习记录(一)

这一块主要是了解linux系统驱动部分,编译镜像相关的知识,这里记录一下。 使用板子如下: 教程用的这一个版本: 1、基本环境搭建 这个比较简单,只是注意一下就是正点原子的教程用了一个NFS文件系统,简单来…

MongoDB介绍及安装

文章目录 MongoDB介绍什么是MongoDBMongoDB技术优势MongoDB应用场景 MongoDB快速开始linux安装MongoDB启动MongoDB Server关闭MongoDB服务 Mongo shell使用mongo shell常用命令数据库操作集合操作 安全认证创建管理员账号常用权限创建应用数据库用户 Docker安装MongoDB工具官方…

物流平台如何与电商平台进行自动化流程管理

为什么要实现物流与电商平台进行自动化管理 实现物流平台与电商平台的自动化流程管理对企业和消费者都有着重要的意义,比如以下几点: 提高效率:自动化流程管理可以减少人为操作的错误和延误,提高订单处理和物流配送的效率。通过定…

What is Rust? Why Rust?

why Rust? 目前,Rust 变得越来越流行。然而,仍然有很多人(和公司!)误解了 Rust 的主张价值是什么,甚至误解了它是什么。在本文中,我们将讨论 Rust 是什么以及为什么它是一种可以增强…

Pytest单元测试框架

第一章、pytest概述 Pytest is a framework that makes building simple and scalable tests easy. Tests are expressive and readable—no boilerplate code required. Get started in minutes with a small unit test or complex functional test for your application or l…

Linux提权:Docker组挂载 Rsync未授权 Sudo-CVE Polkit-CVE

目录 Rsync未授权访问 docker组挂载 Sudo-CVE漏洞 Polkit-CVE漏洞 这里的提权手法是需要有一个普通用户的权限,一般情况下取得的webshell权限可能不够 Rsync未授权访问 Rsync是linux下一款数据备份工具,默认开启873端口 https://vulhub.org/#/envir…

第九节HarmonyOS 常用基础组件17-ScrollBar

1、描述 滚动条组件ScrollBar,用于配合可滚动组件使用,如List、Grid、Scroll。 2、接口 可包含子组件 ScrollBar(value:{scroller:Scroller, direction?: ScrollBarDirection, state?: BarState}) 3、参数 参数名 参数类型 必填 描述 scrolle…

148基于matlab的带有gui的轮轨接触几何计算程序

基于matlab的带有gui的轮轨接触几何计算程序,根据不同的踏面和轨头,计算不同横移量下面的接触点位置。程序已调通,可直接运行。 148 matlab 轮轨接触 横移量 (xiaohongshu.com)

Android App开发基础(2)—— App的工程结构

本专栏文章 上一篇 Android开发修炼之路——(一)Android App开发基础-1 2 App的工程结构 本节介绍App工程的基本结构及其常用配置,首先描述项目和模块的区别,以及工程内部各目录与配置文件的用途说明;其次阐述两种级别…

【qt】switchBtn

方法1 在qtdesigner中设置按钮图标的三个属性,normal off 、normal on和checkabletrue。 from PyQt5.QtWidgets import * from PyQt5.QtGui import * from PyQt5.QtCore import * from PyQt5 import uic from switchBtn import Ui_Dialogclass Test(QDialog, Ui_…

如何使用Docker部署火狐浏览器并实现无公网ip远程访问

文章目录 1. 部署Firefox2. 本地访问Firefox3. Linux安装Cpolar4. 配置Firefox公网地址5. 远程访问Firefox6. 固定Firefox公网地址7. 固定地址访问Firefox Firefox是一款免费开源的网页浏览器,由Mozilla基金会开发和维护。它是第一个成功挑战微软Internet Explorer浏…

fastapi报错

初始化报错,非常低级错,扇自己10八张 app FastApi()

Java 集合 02 综合练习+基本数据类型对应的包装类

练习1、 自己写的代码&#xff1a; import java.util.ArrayList; public class practice {public static void main(String[] args) {//定义一个集合ArrayList<String> list new ArrayList<>();list.add("aaa");list.add("bbb");list.add(…

蓝桥杯备战——8.DS1302时钟芯片

1.分析原理图 由上图可以看到&#xff0c;芯片的时钟引脚SCK接到了P17,数据输出输入引脚IO接到P23,复位引脚RST接到P13。 2.查阅DS1302芯片手册 具体细节还需自行翻阅手册&#xff0c;我只截出重点部分 总结&#xff1a;数据在上升沿写出&#xff0c;下降沿读入&#xff0c;…

C# IP v4转地址·地名 高德

需求: IPv4地址转地址 如&#xff1a;输入14.197.150.014&#xff0c;输出河北省石家庄市 SDK: 目前使用SDK为高德地图WebAPI 高德地图开放平台https://lbs.amap.com/ 可个人开发者使用&#xff0c;不过有配额限制。 WebAPI 免费配额调整公告https://lbs.amap.com/news/…