pytest-教程-14-fixture之yield关键字

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

上一小节我们学习了pytest conftest.py文件的使用方法,本小节我们讲解一下fixture的yield关键字实现teardown后置操作。

当我们运行我们的测试时,我们会希望确保它们自己清理干净,这样它们就不会与任何其他测试混淆(同时我们也不会留下大量的测试数据来使系统膨胀)。pytest中的 Fixtures 提供了一个非常有用的拆卸系统,它允许我们定义每个 Fixture 自行清理所需的特定步骤。分别是通过yield或addfinalizer完成teardown清理,下面分别对这两种方式进行展开说明。

yield方式完成清理(推荐)

使用yield的fixture和普通的fixture基本差不多,但还是有以下两点:

  • return改换成yield。
  • 需要做清理的代码写在yield的下面。

使用yield后,fixture的执行顺序是这样的,如有两个fixture函数,fixture1和fixture2,先由pytest计算出fixture的线性顺序,它将运行每个fxiture直到它返回,然后移动到列表中的下一个夹具做同样的事情。等待测试完成后,pytest将返回到fxiture列表,但顺序相反,如果开始先执行了fixture1再执行了fixture2,那么后面就是先执行fixture2在执行fixture1.

示例:

test_demo.py

import pytest


@pytest.fixture
def get_token():
    print("\n请求获取token\n")
    yield
    # 这里写你的清理代码
    print("\n注销token\n")


def test_demo(get_token):
    print("测试用例")

运行结果:

============================= test session starts =============================
collecting ... collected 1 item

test_demo.py::test_demo 
请求获取token

PASSED                                           [100%]测试用例

注销token



============================== 1 passed in 0.02s ==============================

注:通过结果展示,我们可以清楚的看到我们的清理代码在测试用例运行过后运行了。这种方式比我们用setup和teardown要好很多,可以抽取公共代码,减少代码冗余。

多个yield的fixture执行顺序

test_demo.py

import pytest


@pytest.fixture
def fn1():
    print("\n我是fn1,我在yield前面\n")
    yield 1
    print("\n我是fn1,我在yield后面\n")


@pytest.fixture
def fn2(fn1):
    print("\n我是fn2,我在yield前面\n")
    yield 2
    print("\n我是fn2,我在yield后面\n")


def test_demo(fn2):
    print("\n我是测试用例\n")

运行结果:

============================= test session starts =============================
collecting ... collected 1 item

test_demo.py::test_demo 
我是fn1,我在yield前面


我是fn2,我在yield前面

PASSED                                           [100%]
我是测试用例


我是fn2,我在yield后面


我是fn1,我在yield后面



============================== 1 passed in 0.02s ==============================

注:通过上述例子,我们可以很明显知道yield的fixture的执行顺序,pytest先计算出线性顺序,先执行了fn1,然后再执行了fn2,再执行test,清理时就先执行的fn2,然后再执行fn1,与开始的顺序相反。

yield遇到异常

1、如果其中一个用例在执行时出现异常,不影响yield后面的teardown执行,运行结果互不影响,并且全部用例执行完之后,yield唤起teardown操作。

test_demo.py

import pytest


@pytest.fixture(scope="module")
def open():
    print("打开浏览器,并且打开百度首页")
    yield
    print("执行teardown!")
    print("最后关闭浏览器")


def test_s1(open):
    print("用例1:搜索python-1")

    # 如果第一个用例异常了,不影响其他的用例执行
    raise NameError  # 模拟异常


def test_s2(open):
    print("用例2:搜索python-2")


def test_s3(open):
    print("用例3:搜索python-3")


if __name__ == "__main__":
    pytest.main(["-s", "test_f1.py"])

运行结果:

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

test_demo.py::test_s1 打开浏览器,并且打开百度首页
FAILED                                             [ 33%]用例1:搜索python-1

test_demo.py:11 (test_s1)
open = None

    def test_s1(open):
        print("用例1:搜索python-1")
    
        # 如果第一个用例异常了,不影响其他的用例执行
>       raise NameError  # 模拟异常
E       NameError

test_demo.py:16: NameError

test_demo.py::test_s2 PASSED                                             [ 66%]用例2:搜索python-2

test_demo.py::test_s3 PASSED                                             [100%]用例3:搜索python-3
执行teardown!
最后关闭浏览器


========================= 1 failed, 2 passed in 0.24s =========================

2、但是fixture函数如果在setup执行期间发生异常,那么pytest是不会去执行yield后面的teardown内容。

test_demo.py

import pytest


@pytest.fixture(scope="module")
def open():
    10 / 0
    print("打开浏览器,并且打开百度首页")
    yield
    print("执行teardown!")
    print("最后关闭浏览器")


def test_s1(open):
    print("用例1:搜索python-1")

    # 如果第一个用例异常了,不影响其他的用例执行
    raise NameError  # 模拟异常


def test_s2(open):
    print("用例2:搜索python-2")


def test_s3(open):
    print("用例3:搜索python-3")


if __name__ == "__main__":
    pytest.main(["-s", "test_f1.py"])

运行结果:

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

test_demo.py::test_s1 ERROR                                              [ 33%]
test setup failed
@pytest.fixture(scope="module")
    def open():
>       10 / 0
E       ZeroDivisionError: division by zero

test_demo.py:6: ZeroDivisionError

test_demo.py::test_s2 ERROR                                              [ 66%]
test setup failed
@pytest.fixture(scope="module")
    def open():
>       10 / 0
E       ZeroDivisionError: division by zero

test_demo.py:6: ZeroDivisionError

test_demo.py::test_s3 ERROR                                              [100%]
test setup failed
@pytest.fixture(scope="module")
    def open():
>       10 / 0
E       ZeroDivisionError: division by zero

test_demo.py:6: ZeroDivisionError


============================== 3 errors in 0.27s ==============================

yield关键字+with上下文管理器的结合使用

yield 关键字 也可以配合 with 上下文管理器 语句使用。【使得代码更加精简】

示例

import pytest
import smtplib

@pytest.fixture(scope="module")
def smtp_connection():
    with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection:
        yield smtp_connection

使用addfinalizer方法完成清理

除了yield可以实现teardown,我们也可以通过 request.addfinalizer() 的方式去注册终结函数来实现 teardown 用例的后置操作。addfinalizer 的用法跟 yield 是不同的, addfinalizer 需要你去注册一个或多个作为终结器使用的函数。

例如:增加一个函数 fin,并且注册成终结函数。

test_demo.py

import pytest


@pytest.fixture(scope="module")
def test_addfinalizer(request):
    # 前置操作setup
    print("\n===打开浏览器===\n")
    test = "test_addfinalizer"

    def fin():
        # 后置操作teardown
        print("\n===关闭浏览器===\n")

    request.addfinalizer(fin)
    # 返回前置操作的变量
    return test


def test_case(test_addfinalizer):
    print("===最新用例===", test_addfinalizer)

运行结果:

============================= test session starts =============================
collecting ... collected 1 item

test_demo.py::test_case 
===打开浏览器===

PASSED                                           [100%]===最新用例=== test_addfinalizer

===关闭浏览器===



============================== 1 passed in 0.02s ==============================

yield 与 addfinalizer 用法的区别

① addfinalizer 可以注册多个终结函数。当注册多个终结函数时,用例的后置操作同时会执行完所有的终结函数。

【注意】终结函数(用例后置操作函数)的执行顺序与其在fixture函数中注册的顺序相反(即先注册的终结函数后执行,后注册的终结函数先执行)

示例:

import pytest


@pytest.fixture()
def demo_addfinalizer(request):
    print("====setup====")

    def fin1():
        print("====teardown1====")

    def fin2():
        print("====teardown2====")

    def fin3():
        print("====teardown3====")

    # 注册fin1、fin2、fin3为终结函数
    request.addfinalizer(fin1)
    request.addfinalizer(fin2)
    request.addfinalizer(fin3)


def test_case1(demo_addfinalizer):
    print("====执行用例test_case1====")


def test_case2(demo_addfinalizer):
    print("====执行用例test_case2====")


def test_case3(demo_addfinalizer):
    print("====执行用例test_case3====")


if __name__ == '__main__':
    pytest.main(__file__, '-s')

运行结果:

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

test_demo.py::test_case1 ====setup====
PASSED                                          [ 33%]====执行用例test_case1====
====teardown3====
====teardown2====
====teardown1====

test_demo.py::test_case2 ====setup====
PASSED                                          [ 66%]====执行用例test_case2====
====teardown3====
====teardown2====
====teardown1====

test_demo.py::test_case3 ====setup====
PASSED                                          [100%]====执行用例test_case3====
====teardown3====
====teardown2====
====teardown1====


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

②当执行测试用例时setup前置操作函数的代码执行错误或者发生异常时,addfinalizer 注册的终结函数依旧会执行。

③ yield 关键字可以返回setup前置操作函数中生成的测试数据,且 yield 关键字返回测试数据之后后续的代码依然可以运行。且后续执行的代码充当teardown后置操作函数。

④ addfinalizer 函数可以将一个或者多个函数注册为终结函数(一个或多个函数必须在fixture函数中定义),此时的终结函数为teardown后置操作函数;且最后可以使用 return 关键字返回setup前置操作函数生成的测试数据

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

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

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

相关文章

冒泡经典题

📑前言 本文主要是【】——简单使用的文章,如果有什么需要改进的地方还请大佬指出⛺️ 🎬作者简介:大家好,我是听风与他🥇 ☁️博客首页:CSDN主页听风与他 🌄每日一句:狠…

【Redisson分布式锁】Redisson公平锁实现机制

欢迎关注公众号(通过文章导读关注:【11来了】),及时收到 AI 前沿项目工具及新技术的推送! 在我后台回复 「资料」 可领取编程高频电子书! 在我后台回复「面试」可领取硬核面试笔记! 文章导读地址…

python之双端队列deque

普通队列只能进行队尾插入和出队头的操作,双端队列可以对队头和队尾进行操作,而且相比于list实现的队更具有优越性,list实现在删除和插入时的时间复杂度大约为O(n),而deque的时间复杂度是O(1) 队头操作:append()、pop(…

蓝桥杯练习系统(算法训练)ALGO-986 藏匿的刺客

资源限制 内存限制:256.0MB C/C时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s 问题描述 强大的kAc建立了强大的帝国,但人民深受其学霸及23文化的压迫,于是勇敢的鹏决心反抗。   kAc帝国防…

力扣刷题-82.删除排序链表中的重复元素

给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。 示例 1: 输入:head [1,2,3,3,4,4,5] 输出:[1,2,5]示例 2: 输入:head [1,1,1,2…

计算机组成原理----数据的表示和运算

一:进位计数制 1、进制 B:二进制:0-1 逢二进一、借一当二 O:八进制:0-7 逢八进一 D:十进制:0-9 逢十进一 H:十六进制:0-9、A-F 逢十六进一 r进制2 2、…

第三百八十四回

【toc] 我们在上一章回中介绍了Visibility组件相关的内容,本章回中将介绍Flutter与原生平台通信相关的内容.闲话休提,让我们一起Talk Flutter吧。 概念介绍 在移动开发领域以Android和IOS SDK开发出的应用程序叫原生开发,开发同一个程序需要…

回溯算法01-组合(Java)

1.组合 题目描述 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1: 输入:n 4, k 2 输出: [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4]]示例 2: 输入&#x…

四平方和 刷题笔记

/* 四平方和 直接暴力搜索 可能会超时 使用二分辅助搜索 先枚举出 c*cd*d并存入数组 用式子算出 a*ab*b还剩下多少查找sum数组里面是否存在符合条件的数 查找方式使用二分搜索 当逼近答案后 检查一下是否为所需的数 如果是 直接输出 */ #include <cstring> #includ…

社交媒体的未来图景:探索Facebook的数字化之旅

社交媒体已经成为现代社会不可或缺的一部分&#xff0c;其影响力已经深入到人们生活的方方面面。而在众多社交媒体平台中&#xff0c;Facebook无疑是其中的巨头&#xff0c;其数字化之旅更是引领着整个社交媒体行业的发展方向。本文将深入探讨社交媒体的未来图景&#xff0c;以…

事务处理

Oracle从入门到总裁:https://blog.csdn.net/weixin_67859959/article/details/135209645 事务处理 事务是保证数据完整性的一种手段。 事务具备 ACID 原则&#xff08;包括原子性、一致性、独立性及持久性&#xff09;&#xff0c;保证一个人更新数据的时候&#xff0c;其他…

C# 中 TryParse 将字符串转换为特定类型的方法

在 C# 中&#xff0c;TryParse 是一个用于将字符串转换为特定类型的方法。它用于尝试解析字符串并将其转换为指定类型的值&#xff0c;而不会引发异常。如果解析成功&#xff0c;它将返回 true 并将解析结果存储在输出参数中&#xff1b;如果解析失败&#xff0c;它将返回 fals…

C# 中 Math.Round 数学函数

在 C# 中&#xff0c;Math.Round 是一个数学函数&#xff0c;用于对一个浮点数进行四舍五入操作。它接受一个浮点数作为输入&#xff0c;并返回一个最接近输入值的整数或指定小数位数的浮点数。 Math.Round 方法有多个重载&#xff0c;其中最常用的重载有以下两种形式&#xf…

就业班 2401--3.5 Linux Day11--DNS解析

DNS域名解析服务 少壮不努力&#xff0c;老大徒伤悲。一、 1.DNS介绍 DNS 是域名系统 (Domain Name System) 的缩写&#xff0c;是因特网的一项核心服务&#xff0c;它作为可以将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便的访问互联网&#xff0c;…

「项目实践」如何实现导购推荐“千人千面”

前言 在电商背景下&#xff0c;为用户提供精准的导购推荐不仅能极大提升购物体验&#xff0c;也成为推动业务增长的关键驱动力。随着人工智能和数据科学的快速发展&#xff0c;实现“千人千面”的个性化推荐已不再是无法触及的梦想。如何在众多商品中准确捕捉用户的偏好&#…

进口及国内细胞分析仪厂家名录大全-贝克曼、安捷伦、希森美康、迈瑞.....

流式细胞仪是一种测量层流中细胞的设备&#xff08;细胞仪&#xff09;&#xff0c;其通过将每个细胞排列在鞘液中&#xff0c;加以激光束照射&#xff0c;可测量散射光和荧光&#xff0c;从而获得有关每个细胞的信息&#xff0c;包含细胞结构&#xff08;如大小、粒度、表面积…

Java知识点总结(二)

ID生成策略 主键自增id 主键自动增长&#xff0c;不用手工设值、数字型&#xff0c;占用空间小、检索非常有利、有顺序&#xff0c;不会重复&#xff0c;但在迁移旧数据是会出现id冲突 UUID 基于时间&#xff0c;计数器和地址生成32位的id redis生成id 原子性自增&#xff0c;并…

记录一次排查负载均衡不能创建的排查过程

故障现象&#xff0c;某云上&#xff0c;运维同事在创建负载均衡的时候&#xff0c;发现可以创建资源&#xff0c;但是创建完之后&#xff0c;不显示对应的负载均衡。 创建负载均衡时候&#xff0c;按f12发现console有如下报错 后来请后端网络同事排查日志发现&#xff0c;是后…

探秘X-XSS-Protection头对抗跨站脚本攻击

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 探秘X-XSS-Protection头对抗跨站脚本攻击 前言XSS攻击的威胁X-XSS-Protection头的作用X-XSS-Protection头的配置报告模式&#xff08;Report-Only&#xff09;攻击与绕过X-XSS-Protection的方法 前言 …

(学习日记)2024.03.04:UCOSIII第六节:main函数+前六节总结

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…
最新文章