Pytest学习教程_装饰器(二)

news2024/11/16 19:24:03

前言

pytest装饰器是在使用 pytest 测试框架时用于扩展测试功能的特殊注解或修饰符。使用装饰器可以为测试函数提供额外的功能或行为。
  以下是 pytest 装饰器的一些常见用法和用途:

装饰器作用
@pytest.fixture用于定义测试用例的前置条件和后置操作。可以创建可重用的测试环境或共享资源,并将其注入到测试函数中。通常,fixture 可以返回所需的对象或执行特定的设置和清理操作。
@pytest.mark.parametrize用于参数化测试函数。通过在装饰的函数上提供参数列表,可以运行多组具有不同输入的测试用例。这样可以轻松地扩展测试范围并减少重复的测试代码。
@pytest.mark.skip使用这个装饰器可以跳过不需要运行的测试用例。可以附带参数来提供跳过测试的原因或条件。
@pytest.mark.skipif类似于 @pytest.mark.skip,这个装饰器可以基于条件来跳过特定的测试用例。可以使用预定义的环境变量、Python 版本、操作系统等作为条件。
@pytest.mark.xfail这个装饰器标记所装饰的测试用例为 “expected failure”(预期失败)。也就是说,预计在某些条件下测试将失败,如果出现预期的失败,将被视为测试通过;如果测试用例没有失败,则会被标记为测试失败。
@pytest.mark.repeat这个装饰器用于将测试用例重复运行多次。可以指定重复次数来确定运行次数。
@pytest.mark.usefixtures使用此装饰器可以在测试函数中直接使用已定义的 fixture,而无需在函数签名中显式声明。这样可以简化测试函数的编写。
@pytest.mark.dependency用于声明测试用例之间的依赖关系,以确保测试用例按正确的顺序执行。

一、@pytest.fixture装饰器

  常用的参数列表及其简要说明:

参数说明
scope指定fixture的作用域,控制fixture的生命周期。可选值包括"function"(默认值,每个测试函数调用一次),“class”(每个测试类调用一次),“module”(每个模块调用一次),或"session"(整个测试会话过程中调用一次)
params为fixture指定不同的参数化值。可以是列表、元组或生成器。
autouse控制fixture是否自动应用于测试用例。如果将其设置为True,则fixture将自动应用于所有使用它的测试用例。
ids为参数化fixture中的每个参数指定一个名称或标识符列表,以便在测试报告中更好地识别不同的参数。
name为fixture指定一个显示名称,用于在测试报告中更好地标识fixture。
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
import time


# @pytest.fixture 声明这是一个夹具
# scope="function" 指定了夹具的作用域为函数级别,也就是每个测试函数都会调用该夹具
# autouse=True 指定夹具为自动使用的,也就是不需要在测试函数中显式地调用夹具,它会自动应用于每个测试函数
@pytest.fixture(scope="function", autouse=True)
def setup_browser():
    """
    设置和关闭浏览器的测试夹具
    """
    driver = webdriver.Chrome()  # 初始化Chrome浏览器驱动
    driver.get("https://www.baidu.com/")  # 打开百度首页
    yield driver  # 返回driver对象供测试使用
    driver.quit()  # 测试结束后关闭浏览器


# @pytest.fixture 声明这是一个夹具
# params=[("csdn"......)] 设置夹具的参数列表,夹具被参数化为两个元组,每个元组包含两个值,表示不同的搜索引擎关键字和期望结果
# name="search_engine" 给夹具指定一个名字,这样在测试函数中可以引用该夹具
# ids=["CSDN", "Baidu Knowledge"] 为每个参数设置一个标识符,这些标识符在测试报告中将用于标识参数化的实例
@pytest.fixture(params=[("csdn", "CSDN - 专业开发者社区"), ("百度知识", "百度知道 - 全球领先中文互动问答平台")],
                name="search_engine",
                ids=["CSDN", "Baidu Knowledge"])
def parametrize_search_engine(request):
    """
    参数化的搜索引擎测试夹具
    """
    return request.param  # 返回包含关键字和期望结果的元组


def test_search_home_page(setup_browser):
    """
    测试在百度首页搜索功能
    """
    assert "百度一下" in setup_browser.page_source  # 检查页面源代码中是否包含"百度一下"


def test_navigation(setup_browser, search_engine):
    """
    测试导航功能,包括输入关键字搜索并选择第一个结果
    """
    driver = setup_browser  # 获取浏览器驱动对象
    keyword, engine = search_engine  # 解包含有关键字和期望结果的元组
    # 输入指定关键字,例如"csdn"或"百度知识"
    driver.find_element(By.CSS_SELECTOR, '#kw').send_keys(keyword)
    # 点击搜索按钮
    driver.find_element(By.CSS_SELECTOR, '[class="bg s_btn"]').click()
    time.sleep(5)
    # 选择搜索结果中的第一个链接进行点击
    driver.find_element(By.CSS_SELECTOR, '[class="result c-container xpath-log new-pmd"] h3 a').click()
    time.sleep(5)
    # 切换到最新窗口的句柄
    driver.switch_to.window(driver.window_handles[-1])

    assert driver.title == engine  # 检查打开的页面标题是否与期望结果一致


def test_autouse_fixture():
    """
    测试autouse=True是否生效
    """
    assert 1 == 1


if __name__ == '__main__':
    pytest.main(['test_run.py', '-v'])

二、@pytest.mark.parametrize装饰器

import pytest


# @pytest.mark.parametrize装饰器定义了一个参数化测试函数,允许我们一次运行多组数据进行测试
# 以下参数列表中的每个数据组都将分别传递给被装饰的测试函数
# 参数化允许我们在不同的输入值之间进行测试,以确保代码在各种情况下都能正常工作
@pytest.mark.parametrize("a, b, expected", [
    (2, 3, 5),  # 测试用例1:当a=2,b=3时,预期的输出结果应为5
    (4, 5, 9),  # 测试用例2:当a=4,b=5时,预期的输出结果应为9
    (6, 7, 13)  # 测试用例3:当a=6,b=7时,预期的输出结果应为13
])
def test_addition(a, b, expected):
    # 断言语句用于检查表达式是否为真,以验证代码的正确性,
    # 如果表达式为False,则会引发AssertionError异常。
    assert a + b == expected  # 检查a + b是否等于预期的输出结果


# pytest.main()函数被调用来运行测试
# 运行名为'test_run.py'的测试文件,并通过'-v'参数显示每个测试用例的执行结果
if __name__ == '__main__':
    pytest.main(['test_run.py', '-v'])

三、@pytest.mark.skip装饰器

import pytest


# 该测试用例标记为 `@pytest.mark.skip` 装饰器,表示该测试尚未实现,应跳过执行
# 跳过的原因被指定为 "尚未实现",这通常用于临时禁用尚未准备好或需要进一步开发的测试
@pytest.mark.skip(reason="Not implemented yet")
def test_multiply():
    assert 3 * 4 == 12


if __name__ == '__main__':
    pytest.main(['test_run.py', '-v'])

四、@pytest.mark.skipif装饰器

import pytest
import platform


# 使用 pytest.mark.skipif 装饰器为测试用例添加条件跳过的标记

@pytest.mark.skipif(platform.system() == "Windows"
                    and platform.release() == "10",
                    reason="跳过 Windows 10 版本的测试用例")
def test_linux_run():
    # 仅在 Linux 系统下运行的测试代码
    assert 2 == 2


@pytest.mark.skipif(platform.system() == "Linux",
                    reason="跳过在 Linux 系统下运行的测试用例")
def test_windows_run():
    # 仅在 Windows 系统下运行的测试代码
    assert 2 == 2


if __name__ == '__main__':
    pytest.main(['test_run.py', '-v'])

在这里插入图片描述
在这里插入图片描述

五、@pytest.mark.xfail装饰器

import pytest


# @pytest.mark.xfail 是Pytest测试框架中的一个装饰器,用于标记预期测试失败的测试用例
# 预期失败的测试用例,因为除以零会引发异常
@pytest.mark.xfail
def test_division():
    assert 1 / 0 == 2


# 预期失败的测试用例,因为除以零不等于1,并且应该引发 ZeroDivisionError 异常
@pytest.mark.xfail(raises=ZeroDivisionError)
def test_division_zero():
    assert 1 / 0 == 1


# 预期失败的测试用例,已知此加法操作会失败
@pytest.mark.xfail(reason="This test is known to fail")
def test_addition():
    assert 2 + 2 == 5


# 预期失败的测试用例,这个测试实际上是正确的,但我们使用 xfail 标记将其标记为预期失败
@pytest.mark.xfail
def test_actually_correct():
    assert 1 + 1 == 2


if __name__ == '__main__':
    pytest.main(['test_run.py', '-v'])

六、@pytest.mark.repeat装饰器

# pip install pytest-repeat 安装扩展插件
import pytest


# @pytest.mark.repeat 是 Pytest 测试框架中的一个装饰器,用于指定一个测试用例的重复运行次数
# 将该测试用例重复执行3次
@pytest.mark.repeat(3)
def test_addition():
    result = 2 + 2
    assert result == 4


if __name__ == '__main__':
    pytest.main(['test_run.py', '-v'])

七、@pytest.mark.usefixtures装饰器

import pytest


@pytest.fixture
def setup_data():
    """
    前置条件 - 准备测试数据
    """
    print("前置条件-->准备测试数据")
    data = "test data"
    yield data
    print("后置条件-->清理测试数据")


def test_example(setup_data):
    """
    示例测试方法,使用了前置条件 fixture 'setup_data'
    """
    data = setup_data
    print("执行示例测试")
    assert data == "test data"


@pytest.mark.usefixtures("setup_data")
def test_method():
    """
    方法测试,使用了前置条件 fixture 'setup_data',但不接收它作为参数
    """
    print("执行方法测试")
    assert 1 + 1 == 2


if __name__ == '__main__':
    pytest.main(['test_run.py', '-s'])
  • 定义了两个测试函数test_example和test_method,它们都使用了名为setup_data的 fixture 作为测试的前置条件
  • 在test_example中,setup_data fixture 被作为测试函数的参数使用。在函数内部,data变量被赋值为setup_data fixture 返回的值,然后用于测试
  • 在test_method中,使用了@pytest.mark.usefixtures装饰器,并将"setup_data"作为参数传递。这样一来,setup_data fixture 的前置条件将在运行test_method之前自动执行。虽然在函数体内没有显式接收setup_data作为参数,但仍可以通过setup_data执行相关操作。
  • 这两种写法分别适用于需要测试数据作为输入和不依赖测试数据作为输入的情况

八、@pytest.mark.dependency装饰器

# pip install pytest-dependency 安装扩展插件
import pytest


@pytest.mark.dependency()
def test_login():
    # 登录测试
    assert True


# 指定 test_login 为依赖
@pytest.mark.dependency(depends=["test_login"])
def test_search():
    # 搜索测试
    assert True


# 指定 test_login 和 test_search 为依赖
@pytest.mark.dependency(depends=["test_login", "test_search"])
def test_checkout():
    # 结账测试
    assert True


if __name__ == '__main__':
    pytest.main(['test_run.py', '-v'])
  • test_login 测试函数没有指定任何依赖关系
  • test_search 测试函数指定了 test_login 作为依赖,因此在运行 test_search 之前,会先运行
    test_login
  • test_checkout 测试函数指定了 test_login 和 test_search 作为依赖,因此在运行test_checkout 之前,会先运行 test_login 和 test_search
  • 这样可以确保测试按正确的顺序运行,并且测试之间的依赖关系得到满足。如果某个测试的依赖失败,那么依赖它的测试也将被跳过执行

九、自定义装饰器

import pytest


# 定义一个自定义装饰器,用于对测试用例进行分组
@pytest.mark.group1
def test_addition():
    assert (1 + 2) == 3


# 定义第二个分组
@pytest.mark.group1
def test_subtraction():
    assert (5 - 3) == 2


# 定义第三个分组
@pytest.mark.group2
def test_multiplication():
    assert (2 * 3) == 6


# 定义第四个分组
@pytest.mark.group2
def test_division():
    assert (10 / 2) == 5


# 如果当前文件被直接执行(而不是被导入为模块),则执行以下代码
if __name__ == '__main__':
    # 运行 Pytest 并指定只运行属于 'group1' 分组的测试用例
    pytest.main(['test_run.py', '-m', 'group1'])
  • 通过使用 @pytest.mark.group1 和 @pytest.mark.group2装饰器,我们可以将测试用例进行逻辑分组。这样做的好处是可以只运行特定分组的测试用例,而不是运行所有测试用例
  • 在上述代码中,test_addition() 和 test_subtraction() 被分组为’group1’,test_multiplication() 和 test_division() 被分组为 ‘group2’
  • 运行以上示例会有警告信息,这时我们需要把group1 和group2 注册到 pytest中,在项目目录中创建一个pytest.ini(如果你尚未创建),并在其中定义自定义标记。下面是在pytest.ini 中注册group1和group2 标记的示例:
[pytest]
markers =
    group1: group1 测试的描述
    group2: group2 测试的描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

读发布!设计与部署稳定的分布式系统(第2版)笔记26_安全性上

1. 安全问题 1.1. 系统违规并不总是涉及数据获取,有时会出现植入假数据,例如假身份或假运输文件 1.2. 必须在整个开发过程中持续地把安全内建到系统里,而不是把安全像胡椒面那样在出锅前才撒到系统上 2. OWASP 2.1. Open Web Application…

DataStructure--Basic

程序设计数据结构算法 只谈数据结构不谈算法就跟去话剧院看梁山伯与祝英台结果只有梁山伯在演,祝英台生病了没来一样。 本文的所有内容都出自《大话数据结构》这本书中的代码实现部分,建议看书,书中比我本文写的全。 数据结构,直…

论文笔记——Influence Maximization in Undirected Networks

Influence Maximization in Undirected Networks ContributionMotivationPreliminariesNotations Main resultsReduction to Balanced Optimal InstancesProving Theorem 3.1 for Balanced Optimal Instances Contribution 好久没发paper笔记了,这篇比较偏理论&…

【笔试强训选择题】Day32.习题(错题)解析

作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:笔试强训选择题 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!! 文章目录 前言 一、Da…

线性代数(应用篇):第五章:特征值与特征向量、第六章:二次型

文章目录 第5章 特征值与特征向量、相似矩阵(一) 特征值与特征向量1.定义2.性质3.求解(1)具体型矩阵试根法、多项式带余除法:三阶多项式分解因式 (2)抽象型矩阵 (二) 相似1.矩阵相似(1)定义(2)性质 2.相似对角化(1)定义(2)相似对角化的条件(n阶矩阵A可相…

自动化运维工具——Ansible

自动化运维工具——Ansible 一、Ansible概述二、ansible 环境安装部署1.管理端安装 ansible2.ansible 目录结构3.配置主机清单4.配置密钥对验证 三、ansible 命令行模块1.command 模块2.shell 模块3.cron 模块4.user 模块5.group 模块6.copy 模块7.file 模块8.hostname 模块9&a…

LeetCode102.Binary-Tree-Level-Order-Traversal<二叉树的层序遍历>

题目: 思路: 写过N叉树的层序遍历,(8条消息) LeetCode429.N-Ary-Tree-Level-Order-Traversal<N 叉树的层序遍历>_Eminste的博客-CSDN博客 使用栈保存每一层的结点。然后每次当前层结束。将这一层的值添加进去res中。…

【Jetpack 之 Lifecycle】

Jetpack 之 Lifecycle 在本系列文章中,我们准备分析Jetpack 架构组件。首先我们从最基础的组件开始: Lifecycle, 可以说Jetpack 大部分架构组件都是基于Lifecycle 建立的,此也为Jetpack 架构组件的基础。 关于此此组件的介绍和简…

回答网友 修改一个exe

网友说:他有个很多年前的没有源码的exe,在win10上没法用,让俺看一下。 俺看了一下,发现是窗体设计的背景色的问题。这个程序的背景色用的是clInactiveCaptionText。clInactiveCaptionText 在win10之前的系统上是灰色,但…

【Ajax】笔记-原生jsonp跨域请求案例

原生jsonp跨域请求 输入框&#xff1a;输入后&#xff0c;鼠标移开向服务端发送请求&#xff0c;返回用户不存在(直接返回不存在&#xff0c;不做判断) JS <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><me…

ubuntu22.04 DNSSEC(加密DNS服务) configuration

/etx/systemd/resolved.conf是ubuntu下DNS解析服务配置文件&#xff0c;systemd为ubuntu下system and service配置目录 step 1——修改resolved.conf参数 管理员权限打开 /systemd/resolved.conf sudo nano /etc/systemd/resolved.conf修改如下&#xff1a; # This file i…

17-C++ 数据结构 - 栈

&#x1f4d6; 1.1 什么是栈 栈是一种线性数据结构&#xff0c;具有后进先出&#xff08;Last-In-First-Out&#xff0c;LIFO&#xff09;的特点。可以类比为装满盘子的餐桌&#xff0c;每次放盘子都放在最上面&#xff0c;取盘子时也从最上面取&#xff0c;因此最后放进去的盘…

68. 文本左右对齐

题目链接&#xff1a;力扣 解题思路&#xff1a;遍历单词数组&#xff0c;确定每一行的单词数量&#xff0c; 之后就可以得到每一个需要补充的空格数量。从而得到单词之间需要补充的空格数量。具体算法如下&#xff1a; 确定每一行的单词数量 初始值&#xff1a; num 0&…

基于springboot+mybatis+jsp日用品商城管理系统

基于springbootmybatisjsp日用品商城管理系统 一、系统介绍二、功能展示1.主页(客户)2.登陆、注册&#xff08;客户&#xff09;3.我的购物车(客户)4.我的订单&#xff08;客户&#xff09;5.我的商铺&#xff08;商家&#xff09;6.商品管理&#xff08;商家&#xff09;7.订单…

【编程规范】一文讲解开发中的命名规范

命名规范 好的代码本身就是注释, 所以我们需要统一命名风格。 ​ 在本文中&#xff0c;将从大到小&#xff0c;从外到内&#xff0c;总结Java编程中的命名规范。文中将会涉及到日常工作中常见的命名示例&#xff0c;如包命名&#xff0c;类命名&#xff0c;接口命名&#xff0c…

计算机网络——学习笔记

付费版&#xff1a;直接在上面的CSDN资源下载 免费版&#xff1a;https://wwsk.lanzouk.com/ijkcj13tqmyb 示例图&#xff1a;

BIOS相关知识

简介 BIOS&#xff08;Basic Input Output System&#xff09;基本输入输出系统&#xff0c;固化在服务器主板的专用ROM中&#xff0c;是加载在服务器硬件系统上最基本的运行程序。BIOS位于硬件和系统中间&#xff0c;用来初始化硬件&#xff0c;为操作系统运行做准备 功能 …

C语言进阶——文件的打开(为什么使用文件、什么是文件、文件的打开和关闭)

目录 为什么使用文件 什么是文件 程序文件 数据文件 文件名 文件的打开和关闭 文件指针 打开和关闭 为什么使用文件 在之前学习通讯录时&#xff0c;我们可以给通讯录中增加、删除数据&#xff0c;此时数据是存放在内存中&#xff0c;当程序退出的时候&#xff0c;通讯…

【算法提高:动态规划】1.1 数字三角形模型

文章目录 例题列表1015. 摘花生1018. 最低通行费1027. 方格取数&#xff08;两条路径同时走&#xff09;⭐⭐⭐⭐⭐275. 传纸条&#xff08;转换成 两条路径同时走&#xff09; 例题列表 1015. 摘花生 https://www.acwing.com/problem/content/1017/ 状态要么从左转移过来&…

基于双层优化的大型电动汽车时空调度(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…