Python的pytest框架(4)--参数化测试

在 pytest 测试框架中,参数化测试(Parametrized Testing)意味着将一个测试用例设计为能够接受不同输入数据(参数)并分别执行,以验证被测试代码在面对多种情况时的行为是否符合预期。参数化测试的核心理念是通过复用相同的测试逻辑,但使用不同的输入数据集来增加测试覆盖率,减少代码重复,并提高测试的灵活性和效率。该篇文章就如何使用pytest进行参数化配置来深入解析:

目录

一、参数化测试概念

二、使用 pytest.mark.parametrize

1、基本用法

2、多层参数化:组合数据示例

3、从外部文件加载参数数据(重要)

4、结合 fixture 进行参数化

5、动态参数生成

6、参数化与条件跳过


一、参数化测试概念

参数化测试是一种软件测试策略,它允许测试人员或开发人员使用一组预定义的输入数据集合来运行相同的测试逻辑。这意味着一个测试用例可以被设计为接受不同参数,并根据这些参数执行相应的测试操作。这种方法有助于提高测试覆盖率,确保程序在多种数据条件下的行为正确性,同时减少了编写重复测试代码的工作量。

在参数化测试中,测试脚本保持不变,但其执行时使用的数据集可以灵活变化。这些数据集可能包括边界条件、异常情况、典型用户输入、负测试用例等。通过参数化,测试团队可以系统地遍历各种预期和非预期的输入情况,确保软件在面对多种输入时都能稳定、准确地响应。

二、使用 pytest.mark.parametrize

1、基本用法

pytest的参数化主要通过 pytest.mark.parametrize 装饰器实现。这个装饰器允许你为测试函数指定一组或多组不同的输入数据和预期输出(如果有),从而生成多个独立的测试用例。

基本用法如下:

import pytest

# 假设有一个待测试的函数 `add(a, b)`
def add(a, b):
    return a + b

# 使用 @pytest.mark.parametrize 装饰器参数化测试函数
@pytest.mark.parametrize("a, b, expected_sum", [
    (1, 2, 3),
    (0, 5, 5),
    (-1, -2, -3),
    # ... 更多测试数据
])
def test_add(a, b, expected_sum):
    result = add(a, b)
    assert result == expected_sum

在这个例子中,pytest.mark.parametrize 接受三个参数:

参数名:一个由逗号分隔的字符串列表,表示要传递给测试函数的参数名。在这个例子中是 "a, b, expected_sum"。

数据集:一个嵌套列表,其中每个内部元组对应一组测试数据。元组中的元素按照参数名的顺序与测试函数的参数对应。例如,元组 (1, 2, 3) 表示 a=1, b=2, expected_sum=3。

当 pytest 运行时,它会为每组数据生成一个单独的测试用例(我们写了三组数据也就是会生成三个单独的测试用例),并调用 test_add 函数,传入相应的参数值。这样,即使测试逻辑相同(即检查 add() 函数的输出是否等于预期和),由于使用了不同的输入数据,实际执行的是多个独立的测试。

通过 ids 关键字参数,可以为生成的测试用例提供易读的名称,尤其是在测试数据难以从参数值直接推断的情况下:

import pytest

#场景一:简单字符串列表
test_data = [(1, 2), (3, 4), (5, 6)]
ids = ["Case1", "Case2", "Case3"]

@pytest.mark.parametrize("a, b", test_data, ids=ids)
def test_multiply_numbers(a, b):
    assert a * b == a + b
'''
在这个例子中,ids 参数是一个包含三个字符串的列表,分别对应 test_data 中的三个元组。测试报告将显示为 "Case1", "Case2", "Case3" 而不是默认的参数值。
'''

#场景二:基于参数值生成名称
test_data = [(1, 2, 3), (4, 5, 9)]

@pytest.mark.parametrize("a, b, expected_sum", test_data, ids=lambda params: f"a={params[0]}_b={params[1]}_sum={params[2]}")
def test_addition(a, b, expected_sum):
    assert a + b == expected_sum
'''
这里的 ids 参数是一个 lambda 函数,它接收每个参数元组 params,并使用 f-string 格式化输出一个描述性的字符串,如 "a=1_b=2_sum=3"。这样,测试报告中的用例名称将明确反映每个测试用例的具体参数值。
'''

#场景三:自定义命名规则
def generate_id(data):
    username, password = data
    return f"{username}_{password[:3]}_login"

test_data = [("user1", "pass123"), ("user2", "passabc")]

@pytest.mark.parametrize("username, password", test_data, ids=generate_id)
def test_login(username, password):
    # 实现登录逻辑的断言
    pass
'''
在这个例子中,generate_id 函数接收包含用户名和密码的元组,返回一个如 "user1_pass1_登录" 或 "user2_passa_登录" 样式的字符串。这个自定义函数被直接用作 ids 参数,为每个测试用例生成具有特定格式的名称。
'''

#场景四:处理中文及其他非 ASCII 字符
test_data = [(1, "你好"), (2, "世界")]
ids = [f"Case_{i}_({text})".encode("utf-8") for i, text in enumerate(test_data, start=1)]

@pytest.mark.parametrize("num, text", test_data, ids=ids)
def test_chinese_text(num, text):
    assert isinstance(text, str)
'''在这个例子中,ids 列表中的每个字符串都被显式地 encode() 成 UTF-8 编码。如果控制台能够正确处理 UTF-8 输出,则无需解码;否则,可能需要在显示时进行 decode()。'''

2、多层参数化:组合数据示例

有时需要对多个参数进行不同的组合,形成复杂的测试矩阵。这可以通过应用多个 pytest.mark.parametrize 装饰器来实现。它们提供的数据集会按照笛卡尔积的方式组合,生成更复杂的测试用例矩阵:

def multiply(a, b):
    return a * b

# 参数化 `a` 的数据
a_values = [-2, 0, 1, 2]

# 参数化 `b` 的数据
b_values = [0, 1, 2, 3]

# 为 `a` 和 `b` 分别应用参数化,生成所有组合的测试用例
@pytest.mark.parametrize("a", a_values)
@pytest.mark.parametrize("b", b_values)
def test_multiply(a, b):
    expected_product = a * b
    assert multiply(a, b) == expected_product

在这个示例中,我们分别为 a 和 b 定义了参数化数据。每个参数的装饰器都会生成一系列测试用例。由于两个装饰器同时作用于 test_multiply() 函数,pytest 会按照笛卡尔积的方式组合 a 和 b 的所有可能值,生成 4 × 4 = 16 个测试用例。

3、从外部文件加载参数数据(重要)

在实际项目中,参数数据可能会非常多,或者需要根据实际情况动态调整。此时,可以从外部文件(如 CSV、JSON)加载参数数据。使用外部文件通常会需要使用第三方库来操作读取文件数据:

import csv
import pytest

# 假设有一个 `test_data.csv` 文件,内容如下:
# a,b,expected_result
# 1,2,3
# 4,5,20
# ...

def load_test_data(file_path):
    test_data = []
    with open(file_path, newline='') as csvfile:
        reader = csv.DictReader(csvfile)
        for row in reader:
            test_data.append((int(row['a']), int(row['b']), int(row['expected_result'])))
    return test_data

test_data = load_test_data('test_data.csv')

@pytest.mark.parametrize("a, b, expected_result", test_data)
def test_multiply_from_file(a, b, expected_result):
    assert multiply(a, b) == expected_result

这里,我们定义了一个 load_test_data() 函数,它从指定的 CSV 文件中读取参数数据,并将其转换为一个包含 (a, b, expected_result) 元组的列表。然后,将这个列表作为参数数据传递给 pytest.mark.parametrize。这样,测试数据就可以独立于测试代码进行管理和更新。

4、结合 fixture 进行参数化

pytest 的 fixture 可以用来提供测试所需的共享资源或预置条件。结合参数化,可以动态生成 fixture 数据。

import pytest

@pytest.fixture(params=[1, 2, 3, 4, 5])
def input_value(request):
    return request.param

def test_with_fixture(input_value):
    assert input_value > 0

在这个例子中,我们定义了一个 fixture input_value,并使用 params 参数对其进行参数化。pytest 会为 fixture 指定的每个参数值生成一个独立的 fixture 实例,并将其注入到使用该 fixture 的测试函数中。因此,test_with_fixture() 函数会被执行五次,每次使用 input_value fixture 提供的一个不同的正整数。

5、动态参数生成

除了使用静态定义的参数数据集,还可以编写函数或使用生成器来动态生成参数值。这在处理大量数据、随机数据或基于某种规则生成的测试数据时特别有用,以下是一个简单的例子:

import random
import pytest

def generate_random_integers(count=10):
    return [(random.randint(-100, 100), random.randint(-100, 100)) for _ in range(count)]

@pytest.mark.parametrize("x, y", generate_random_integers())
def test_complex_operation(x, y):
    result = complex_operation(x, y)
    assert result.is_valid()  # 假设有一个is_valid()方法来验证结果有效性

对于需要大量或复杂参数组合的场景,可以使用生成器函数(yield 语句)来动态生成参数。这种方式特别适用于有特定规律或算法生成的参数集:

import pytest
import random

def generate_random_inputs():
    for _ in range(10):
        input = random.randint(1, 10**999)
        expected = input * 2
        yield input, expected

@pytest.mark.parametrize("input, expected", generate_random_inputs())
def test_doubling_function(input, expected):
    assert doubling_function(input) == expected

在这个例子中,generate_random_inputs 是一个生成器函数,它每次 yield 一对随机的 input 和对应的 expected 值。parametrize 会遍历这些生成的值,为每一对生成一个测试实例。

6、参数化与条件跳过

可以结合 pytest.mark.skipif 或 pytest.mark.xfail 根据特定条件(如环境变量、版本依赖等)有条件地跳过某些参数组合或标记其为预期失败。

import pytest

@pytest.mark.parametrize("input, expected", ...)
@pytest.mark.skipif(condition, reason="由于缺少依赖项而跳过")
def test_feature(input, expected):
    ...

@pytest.mark.parametrize("input, expected", ...)
@pytest.mark.xfail(condition, reason="由于已知问题导致的预期故障")
def test_flaky_behavior(input, expected):
    ...

condition是一个布尔值或可以计算出布尔值的表达式,pytest 在执行测试之前会先评估这个条件。当条件为 True 时,即使测试实际通过了,pytest 也会将其报告为预期失败(xfail)。反之,若条件为 False,则测试按照正常流程执行,无论其实际结果是通过还是失败。

也可以是一个可调用对象(如函数、lambda 表达式等),pytest 会在测试执行前调用它。该可调用对象应不接受任何参数,并返回一个布尔值。返回 True 时,测试被视为预期失败;返回 False 时,测试按正常流程执行。

希望以上内容能帮助大家理解使用pytest进行参数化操作!

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

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

相关文章

可以与 FastAPI 不分伯仲的 Python 著名的 Web 框架

正如你所理解的,任何领域都不可能停止进步,不断使用相同的工具意味着不思进取。这一点在信息技术领域,尤其是网络开发行业非常明显。 关于网络框架,不论是 Django 和 Flask 等传统框架还是 Python 的新型高级框架,一直…

开源项目|使用go语言搭建高效的环信 IM Rest接口(附源码)

项目背景 环信 Server SDK 是对环信 IM REST API 的封装, 可以节省服务器端开发者对接环信 API 的时间,只需要配置自己的 App Key 相关信息即可使用。 环信目前提供java和PHP版本的Server SDK,此项目使用go语言对环信 IM REST API 进行封装…

B端:再探列表页,这20个组件能让列表页功能完备,体验过关。

有很多小伙伴反馈设计列表页的时候,好看是好看了,但是用户体验不佳,处理数据十分不方便,这样好看也就失去了意义,贝格前端工场分析这个原因大概率是没有用好列表页的组件,丢三落四的情况比较多导致的&#…

RK3588 Android13 鼠标风格自定义动态切换

前言 电视产品,客户提供了三套鼠标图标过来,要求替换系统中原有丑陋风格且要支持动态切换, 并且在 TvSetting 中要有菜单,客户说啥就是啥呗,开整。 效果图 test framework 部分修改文件清单 png 为鼠标风格资源图片,这里就不提供了,可自由找一个替换一下就行 framew…

「Word 论文排版」插入分节符导致word转PDF后出现空白页

问题 word转PDF后出现空白页 解决 但是此方法会让页面页脚标记出错 TODO 如下图所示 在论文目录后有一个分节符,转成PDF之后就多了一个空白页 文件-打印-页面设置-选中封面那一页-版式-从偶数页开始 再导出空白页就没了

旅游陪同翻译难吗, 旅游翻译英译中哪家好?

近来,随着中国旅游业的蓬勃发展,旅游陪同翻译的需求也水涨船高,这些专业的翻译服务者为中外游客搭建起友谊的桥梁,引领他们共同探索中国这片古老而神秘的土地 。那么,旅游陪同翻译英译中难吗?我们如何在众多…

iTwin Capture Modeler-23中文版下载地址及安装教程

文章目录 一、iTwin Capture Modeler23中文版安装教程二、iTwin Capture Modeler23中文版下载地址一、iTwin Capture Modeler23中文版安装教程 1. 解压安装包。订阅专栏(可获取专栏内所有文章阅读权限与软件安装包)后,从文末获取安装包解压,如下所示: 2. 右击安装包,选择以…

MQ技术选型

消息队列中间件是分布式系统中重要的组件,主要解决应用耦合、异步消息、流量削锋等问题。它可以实现高性能、高可用、可伸缩和最终一致性架构,是大型分布式系统不可缺少的中间件。 RabbitMQ 特点: RabbitMQ 相当轻量级的消息队列&#xff…

探索 2024 年促进业务发展的最佳定制 GPT

打造个性化客户体验: GPT模型在电子商务中的应用 介绍 在商业领域,人工智能(AI)的出现开创了创新和效率的新时代。 最具影响力的人工智能技术之一是自定义 GPT 模型。 本文探讨了自定义 GPT 模型如何显着促进各种业务运营。 自定义 GPT 模型的…

零售数据分析之补货表怎么做?

做零售数据分析,不仅要关注销售,还需要注意补货分析。通过补货分析了解不同商品要在什么时候进行补货,提前做好库存准备,以免出现补货不及时的失误。那么,零售数据分析中的补货表该怎么做?需要计算分析哪些…

Embedding例子:简单NN网络、迁移学习例子

一、简单例子:构造简单NN网络生成Embedding 1、pytorch例子 2、tensorflow例子 # 1导入模块 import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Embedding import numpy as np# 2构建语料库 corpus[[…

前端框架深度技术革新历程:从原生DOM操作到数据双向绑定、虚拟DOM等框架原理深度解析,Web开发与用户体验的共赢

前端的发展与前端框架的发展相辅相成,形成了相互驱动、共同演进的关系。前端技术的进步不仅催生了前端框架的产生,也为其发展提供了源源不断的动力。 前端的发展 前端,即Web前端,是指在创建Web应用程序或网站过程中负责用户界面…

爱普生无源晶体MC-146特点,应用介绍

爱普生的MC-146系列产品,应用广泛,如小的通讯社本,工业控制等等,几乎涉及各个领域。属于现阶段性价比非常不错的一个系列。晶体振荡器有很多种类,无源晶体其中最简单的一个类。在每个设计中,要用到非常多的…

LabVIEW供热管道泄漏监测与定位

LabVIEW供热管道泄漏监测与定位 在现代城市的基础设施建设中,供热管道系统起着极其重要的作用。然而,管道泄漏问题不仅导致巨大的经济损失,还对公共安全构成威胁。因此,开发一种高效、准确的管道泄漏监测与定位技术显得尤为关键。…

Mac 部署 GPT-2 预训练模型 gpt2-chinese-cluecorpussmall

文章目录 下载 GPT-2 模型快速开始 GPT-2 下载 GPT-2 模型 https://huggingface.co/uer/gpt2-chinese-cluecorpussmall git clone https://huggingface.co/uer/gpt2-chinese-cluecorpussmall # 或单独下载 LFS GIT_LFS_SKIP_SMUDGE1 git clone https://huggingface.co/uer/gpt…

清洗机什么牌子好质量过硬、四大公认最好用的超声波清洗机

现在十个人中有九个人都是戴眼镜的,眼镜已成为我们生活中不可或缺的一部分。无论是用于视力矫正,还是作为时尚配饰,眼镜都承载着重要的角色。然而,很多人在享受眼镜带来便利的同时,却忽视了对眼镜的适当清洁和维护。殊…

Trivy离线扫描:容器安全实践指南

一、Trivy简介 1.1 Trivy 概述 Trivy 是一款全面多功能的安全扫描器。Trivy具有寻找安全问题和目标的扫描器。现已经被 Github Action、Harbor 等主流工具集成,Trivy支持大多数流行的编程语言、操作系统和平台的扫描,应该是该领域目前目前采用最广的开…

数据可视化插件echarts【前端】

数据可视化插件echarts【前端】 前言版权开源推荐数据可视化插件echarts一、如何使用1.1 下载1.2 找到js文件1.3 入门使用1.4 我的使用 二、前后端交互:入门demo2.1 前端htmljs 2.2 后端entitycontrollerservicemapper 三、前后端交互:动态数据3.1 前端j…

ChatGPT AI 教我用python实现工作久坐定时提醒工具,防猝死!

日常工作学习久坐的危害很大,非常伤害颈椎和腰椎,严重危害上班族的身体健康,强烈建议久坐后间隔一小时活动一下,最好是能够调整好自己坐姿,行为举止一定要正确,为了您的老腰! 久坐一族&#xff…

Linux——日志的编写与线程池

目录 前言 一、日志的编写 二、线程池 1.线程池基本原理 2.线程池作用 3.线程池的实现 前言 学了很多线程相关的知识点,线程控制、线程互斥、线程同步,今天我们将他们做一个总结,运用所学知识写一个较为完整的线程池,同时…