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

全网最牛自动化测试框架系列之pytest(7)-yield与终结函数

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

通过上一篇文章,我们已经知道了pytest中,可以使用Fixture来完成运行测试用例之前的一些操作如连接数据库,以及测试执行之后自动去做一些善后工作如清空脏数据、关闭数据库连接等。

我们已经学会了fixture函数的简单用法,但其实fixture还提供了两种非常优雅高效的写法,来完成测试执行前的处理操作与执行后的处理操作,即使用yieldaddfinalizer来实现。

yield

在fixture中的关键字yield主要有两个作用:

  • yield代替return进行参数的传递
  • 起到代码的分割作用,yield之前的代码为setup的作用,yield之后的代码为teardown的作用

yield 与 return

在 pytest 的fixture函数中可以使用yield代替return进行返回,示例如下:

import pytest

@pytest.fixture(autouse=True)
def fixture_one():
    print("执行fixture_one")
    yield 1
    
def test_e(fixture_one):
    print("执行test_e")
    print(fixture_one)
    assert fixture_one == 1


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

运行结果如下:

test_case_4.py::test_e 
执行fixture_one
PASSED                                            [100%]执行test_e
1


============================== 1 passed in 0.12s ==============================

从运行结果我们能看到fixture_one会返回1并传递给test_e,与return的作用完全一致。但如果仅仅只是这样使用的话,毫无意义,因为使用return足够了。所以,在实际的使用过程中我们一般会在yield后面加上teardown的代码。

yield 与 teardown

yield不进行参数传递

对于不需要在前置操作中返回数据的 fixture 函数,加入yield,那么yield之前的代码为用例执行之前的操作(即setup),yield之后的代码为用例执行之后的操作(即teardown)。示例如下:

import pytest

@pytest.fixture()
def fixture_demo():
    # setup
    print("\n连接数据库")
    yield
    # teardown
    print("清空脏数据")

def test_case(fixture_demo):
    print("执行test_case")
    assert True


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

运行结果如下:

从结果中我们可以看出来,先执行了setup部分,再执行测试用例,最后执行teardown部分。

yield进行参数传递

yield可以将参数传递给测试用例。

假设有这样一个场景,需要用到接口1的返回参数作为接口2的请求参数,即接口2依赖接口1,我们需要写一条测试用例对接口2进行测试,这个时候可以将接口1的请求写在前置中,如果是unittest框架则代码如下:

import unittest
import requests

class TestDemo(unittest.TestCase):

    def setup(self):
        print("请求接口1")
        self.res_1 = requests.get(url=url_1, params=params_1)

    def test_api_2(self):
        print("验证接口2")
        # 将接口1的返回值self.res_1作为请求参数,请求接口2
        res = requests.post(url=url_2, data=self.res_1)
        # 断言
        self.assertEqual(res, "接口2预期的返回结果")

    def teardown(self):
        print("清空脏数据")

pytest框架中使用fixture+yield则可编写如下:

@pytest.fixture()
def get_api_1_result():
    # setup
    res_1 = requests.get(url=url_1, params=params_1)
    yield res_1
    # teardown
    print("清空脏数据")
    

def test_api_2(get_api_1_result):
    print("验证接口2")
    # 将接口1的返回值res_1作为请求参数,请求接口2
    res = requests.post(url=url_2, data=get_api_1_result)
    # 断言
    assert res == "接口2预期的返回结果"

其中,fixture 会先通过yield返回res_1,并传入测试用例test_api_2中,test_api_2运行完成后再去执行yield后面的代码,即执行print("清空脏数据")

通过以上对比unittestsetupteardown以及参数的传递,我们就能很直观的看出pytestyield的使用方式,此处代码仅为示例。

yield 的执行顺序

有时候我们会遇到一个fixture函数调用另一个或多个fixture函数,且这些函数中可能含有yield,我们先看示例,代码如下:

import pytest

@pytest.fixture
def fixture_1():
    print("\n执行fixture_1")
    yield 1
    print("\n执行fixture_1的teardown代码")

@pytest.fixture
def fixture_2(fixture_1):
    print("\n执行fixture_2")
    yield 2
    print("\n执行fixture_2的teardown代码")

@pytest.fixture
def fixture_add(fixture_1, fixture_2):
    print("\n执行fixture_add")
    result = fixture_1 + fixture_2
    yield result
    print("\n执行fixture_add的teardown代码")

    
def test_demo(fixture_add):
    print("\n执行测试函数test_demo")
    assert fixture_add == 3


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

运行结果如下:

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 1 item

test_case_4.py::test_demo 
执行fixture_1

执行fixture_2

执行fixture_add
PASSED                                         [100%]
执行测试函数test_demo

执行fixture_add的teardown代码

执行fixture_2的teardown代码

执行fixture_1的teardown代码


============================== 1 passed in 0.12s ==============================

从结果可以看出:

test_demo 测试函数执行之前:先执行了 fixture_1,再执行fixture_2,最后执行fixture_add,注意此时都是执行yield之前的的代码;

test_demo 测试函数执行之后:先执行了 fixture_add,再执行fixture_2,最后执行fixture_1,注意此时都是执行yield之后的的代码。

因此,当一个fixture函数调用另一个或多个fixture函数,且fixture函数中含有yield时,被测试函数调用时有如下执行顺序:

  • 测试函数执行之前,pytest会根据fixture函数之间的线性关系顺序调用,即依次执行yield之前的代码

  • 而测试函数执行结束后,pytest会根据之前的顺序反方向执行fixture函数中yield之后的代码

finalizer

finalizer即终结器 (终结函数),与unittest中的teardown作用一样,测试用例执行完成后再执行终结器代码。

在pytest中,fixture除了使用 yield 进行 teardown 之外,还可以使用request.addfinalizer()定义finalizer来进行后置操作。

使用addfinalizer,需要在定义 fixture 函数时传入request,并以内嵌函数的形式进行定义。终结函数可以定义一个或多个。

定义单个终结函数

示例如下:

import pytest

@pytest.fixture
def fixture_demo(request):
    print("\nsetup:每个case开始前执行一次")

    # 定义终结函数
    def finalizer_demo():
        print("\nteardown:每个case完成后执行一次")

    # 将finalizer_demo注册为终结函数
    request.addfinalizer(finalizer_demo)


def test_2(fixture_demo):
    print("\n执行test_2")

def test_1(fixture_demo):
    print("\n执行test_1")


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 2 items

test_module_02\test_case_3.py::test_2 
setup:每个case开始前执行一次
PASSED                             [ 50%]
执行test_2

teardown:每个case完成后执行一次

test_module_02\test_case_3.py::test_1 
setup:每个case开始前执行一次
PASSED                             [100%]
执行test_1

teardown:每个case完成后执行一次


============================== 2 passed in 0.03s ==============================

从结果可以看出来,在测试用例执行完后会执行addfinalizer函数,效果与执行yield后的代码一致。

定义多个终结函数

示例如下:

import pytest

@pytest.fixture
def fixture_demo(request):
    print("\nsetup:每个case开始前执行一次")

    # 定义终结函数
    def finalizer_demo_1():
        print("\nteardown1:每个case完成后执行一次")

    def finalizer_demo_2():
        print("\nteardown2:每个case完成后执行一次")

    # 注册为终结函数
    request.addfinalizer(finalizer_demo_1)
    request.addfinalizer(finalizer_demo_2)


def test_2(fixture_demo):
    print("\n执行test_2")

def test_1(fixture_demo):
    print("\n执行test_1")


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

PYTHON 复制 全屏

运行结果如下:

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 2 items

test_module_02\test_case_3.py::test_2 
setup:每个case开始前执行一次
PASSED                             [ 50%]
执行test_2

teardown2:每个case完成后执行一次

teardown1:每个case完成后执行一次

test_module_02\test_case_3.py::test_1 
setup:每个case开始前执行一次
PASSED                             [100%]
执行test_1

teardown2:每个case完成后执行一次

teardown1:每个case完成后执行一次


============================== 2 passed in 0.02s ==============================

CPP 复制 全屏

从结果可以看出,上面示例中测试函数执行完成后,先执行了finalizer_demo_2,后执行finalizer_demo_1

所以, 当有多个终结函数被执行时,执行顺序与注册顺序是相反的

总结

实际项目中,可以视情况进行选择,但一般情况下,推荐使用yield,因为这样代码更加简洁高效,且阅读性更强更容易维护。

 

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

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

【记得(备注“csdn000”)】

【或私信000】

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

项目实战:

大型电商平台:

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

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

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

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

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

相关文章:

  • JSR303校验(1)
  • Nacos - 支持PostgreSQL
  • CFD网格质量评估标准
  • 网课答案公众号题库系统
  • 详解AVL树(二叉搜索平衡树)【C++实现】
  • 网课答案公众号搭建方法
  • 牛客网练习题(函数部分)
  • 仿everything的文件搜索工具项目详解:Part3
  • React中this.setState方法原理解析(详解)
  • jquary
  • nginx-vts监控模块
  • “刷脸”看健康码的电子哨兵,一旦遭破解,让你无路可走
  • var、let、const声明变量和不用var声明变量的区别
  • 【uni-app系列】uni-ui扩展组件和uViewUI的安装使用
  • Python Excel xlsx,xls,csv 格式互转
  • [译]CSS 居中(Center)方法大合集
  • iOS小技巧之UIImagePickerController实现头像选择
  • Java知识点总结(JDBC-连接步骤及CRUD)
  • JS专题之继承
  • Laravel Mix运行时关于es2015报错解决方案
  • learning koa2.x
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • mongo索引构建
  • react-native 安卓真机环境搭建
  • React中的“虫洞”——Context
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • vue-router的history模式发布配置
  • 浮动相关
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 前端面试之CSS3新特性
  • 如何选择开源的机器学习框架?
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • 以太坊客户端Geth命令参数详解
  • 赢得Docker挑战最佳实践
  • 正则表达式小结
  • Mac 上flink的安装与启动
  • 阿里云服务器如何修改远程端口?
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • # .NET Framework中使用命名管道进行进程间通信
  • # 安徽锐锋科技IDMS系统简介
  • (function(){})()的分步解析
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (力扣)循环队列的实现与详解(C语言)
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (全注解开发)学习Spring-MVC的第三天
  • (转)如何上传第三方jar包至Maven私服让maven项目可以使用第三方jar包
  • (转载)Linux网络编程入门
  • .Net Core webapi RestFul 统一接口数据返回格式
  • .NET简谈互操作(五:基础知识之Dynamic平台调用)
  • .net开发时的诡异问题,button的onclick事件无效
  • .net开发引用程序集提示没有强名称的解决办法
  • @Bean, @Component, @Configuration简析
  • @Bean有哪些属性