自动化测试框架pytest系列之强大的fixture功能,为什么fixture强大?一文拆解它的功能参数。(三)

自动化测试框架pytest系列之基础概念介绍(一)-CSDN博客

自动化测试框架pytest系列之21个命令行参数介绍(二)-CSDN博客

接上两篇文章继续 :

3.3 pytest支持的初始化和清除函数

学过unittest的都知道 ,unittest有四个函数 ,分别是 :setUp() 、 tearDown 、setUpClass ,tearDownClass 它们的作用就是在用例运行前和运行后所做的操作 ,往往是在运行前做准备工作 ,运行后做恢复 工作 。

而在pytest中,不仅支持了这四个函数 ,而且又进行了扩展 ,分别为:模块级别、类级别、函数级别、方法级别、方法细化级别,分别如下:

而在pytest中,不仅支持了这四个函数 ,而且又进行了扩展 ,分别为:模块级别、类级别、函数级别、方法级别、方法细化级别,分别如下:

支持方法功能描述
setup_module()在每个模块之前执行
teardown_module()在每个模块之后执行
setup_class()在每个类之前执行,即在一个测试类只运行一次setup_class
teardown_class()在每个类之后执行,即在一个测试类只运行一次teardown_class
setup_function()在每个函数之前执行。
teardown_function()在每个函数之后执行。
setup_method()在每个方法之前执行
teardown_method()在每个方法之后执行
setup()在每个方法之前执行
teardown()在每个方法之后执行

首先要明确的是,这些函数确实很重要 ,举个例子你就知道 ,比如你要做web自动化测试 ,通常的操作是:

  1. 打开浏览器

  2. 运行一条测试用例

  3. 关闭浏览器

  4. 重复上面的3个步骤 ,只到所有测试用例运行完毕 。

好了 ,现在的问题是:这个打开浏览器的操作 ,它本身不属于测试用例 ,但是又是一个必须要做的操作 ,怎么办 ?

你就可以将打开浏览器操作放在setup_method()或者setup函数 ,同样关闭浏览器放在teardown_method()或teardown()中。

所以,在编写测试用例中你几乎都会用到它们 。但是在pytest中,这些函数的实现只是为了习惯unittest用户的使用 ,或者是为了兼容unittest框架的那些函数 。如果你不了解unittest中的setup和teardown ,建议你先看看 。如何搭建接口自动化框架系列之unittest测试框架详解(二) - 知乎 (zhihu.com)

但是你如果用过pytest的fixture的话,你几乎就不想用它们了 ,为什么呢 ? 因为它们有10个函数 ,不好记。而如果使用fixture只需要编写一个函数就可以搞定以上所有的需求 。所以 ,在这里建议你使用fixture就可以了 ,它的强大超出你的想象 。

3.4 强大的fixture

在pytest中 ,fixture是核心的功能 ,通过装饰器所使用的 ,而在pytest中使用的装饰器有很多 ,这个我们后来单独分一个板块再说 。

在这里 ,我们先问自己一个问题 ,为什么这个fixture非常强大 ?这的要从执行测试用例说起 ,因为执行测试用例其实就是如下的3个步骤 :

  1. 做初始化操作

  2. 执行测试用例

  3. 进行清除操作

例如,我要编写一条测试用例 ,一般会在这条用例中编写三个方法 ,分别是:

  • 初始化函数:setUp()

  • 参数化的测试用例 :test_case()

  • 清除函数:tearDown()

那如果是10条测试用例呢 ? 那就是30个方法 ,同样编写1000条测试用例 ,就是3000个方法 。虽然每个方法都大同小异 ,但是你使用其他测试框架是无法把他们聚合到一起的 ,这就为我们编写脚本效率是有大大折扣的 。

但是如果你使用pytest的fixture就可以通过一个函数搞定 ,在这个函数中既可以实现setUp()和tearDown() ,也可以通过参数化将测试数据返回给测试用例。这是什么意思呢 ?我们还是拿上面例子来说明 ,如果你是编写3000个方法 ,而使用fixture的话,只需要编写一个fixture函数可以搞定了 。是不是极大的提升了开发效率呢 ? 这就是fixture的强大之处。这种实现在我的自动化高级课程中就有说明。

虽然我们说了这个fixture的强大 ,如果你没有真正把这个强大功能利用出来 ,那你就浪费了这个功能 。废话少说,接下来我们来介绍fixture这个功能 。

先说这个功能的主要用途 ,就两点 :

  1. 可以进行参数化

  2. 可以解决初始化和清除的操作,在上一小节中介绍的那10个函数 ,都可以通过这个fixture来实现 。

函数格式 :

fixture(scope,autouse,params,ids,name):scope :在什么层级下运行 ,它的值只有 :session ,package ,module ,class ,function(默认值)autouse : 代表的是否自动执行 ,若此参数不加或者设置为False的话,代表不会自动运行 ,设置为True自动执行,无需调用params : 进行的数据参数化 ,参数化的数据就是通过此参数传入到测试用例中。ids : 它是给生成的结果的fixture进行重命名 ,主要是为了好理解 ,前提是必须要有参数params name : 对fixture的函数名起别名, 或者叫重命名 ,没啥大用处 。

使用时只需要将fixture通过装饰器标记到对应的函数上就可以了 。

fixture这个函数中,其中前3个参数是有用的,后面两个无所谓了,可用可不用 。接下来我们介绍它的每个参数使用 :

参数名 :autouse ,它只有两个值,分别是 :

  • True : 如果等于此值 ,此fixture将被自动调用 ,而且都是在测试用例前执行 。

  • False : 该值是此参数的默认值 ,如果等于此值 ,此fixture将不会自动调用,需要主动调用。

接下来我们编写一个fixture函数 ,将autouse设置为True, 里面只打印一句代码 ,然后又编写了两条测试用例 。

​
@pytest.fixture(autouse=True)
def fixture_demo():print("每次运行用例前都执行下这个函数")
​
​
# case1 : 输入正确的用户名和正确的密码进行登录
def test_login_success():print("1")expect_result = 0actual_result = login('admin','123456').get('code')assert expect_result == actual_result
​
​
# case2 : 输入正确的用户名和错误的密码进行登录
def test_password_is_wrong():print("2")expect_reesult = 3actual_result = login('admin','1234567').get('code')assert expect_reesult == actual_result

运行结果 :

通过上面的结果我们看到 ,fixture_demo并没有被主动调用 ,就是因为autouse=Ture ,所以它会被自动调用。

以上我们介绍的是autouse=True的情况 ,那如果它的值是False呢 ? 该如何调用呢 。

首先需要明确一点,如果autouse=False ,这是它的默认值 ,也就是说如果是默认值的话就可以省略 。我们将上面的代码修改下 :

@pytest.fixture(autouse=False)			# 其实这里的autouse可以不传,因为False就是它的默认值
def fixture_demo():return "hello pytest"# case1 : 输入正确的用户名和正确的密码进行登录
def test_login_success():print("1")expect_result = 0actual_result = login('admin','123456').get('code')assert expect_result == actual_result# case2 : 输入正确的用户名和错误的密码进行登录
def test_password_is_wrong(fixture_demo):print("2")print("fixture_demo:{}".format(fixture_demo))expect_reesult = 3actual_result = login('admin','1234567').get('code')assert expect_reesult == actual_result

注意 :我把fixture函数编写为有返回值了 ,同时在test_password_is_wrong方法中传入了这个fixture函数 。相当于把fixture_demo作为测试方法的参数了 。接下来我们看运行结果:

总结:就是将fixture函数的返回值,直接传给测试用例了 ,测试用例可以接受到fixture的返回值 。

当然 ,你也可以同时编写多个fixture函数 ,同时将这些fixture函数传入到一个用例或者多个用例 ,这都没有问题 。

参数名 :scope ,它有5个值,分别是 :

  • session :如果等于此值 ,那么这个fixture在整个项目下只运行一次 ,这个特别适合于登录 ,登录在项目中只需要登录一次。

  • package :如果设置为此值 ,那么这个fixture在这个包下只运行一次 。

  • module : 如果设置为此值 ,那么这个fixture在这个文件中只运行一次 ,文件中可能既有函数又有类 。

  • class : 如果设置为此值 ,那么这个fixture在这个类中只运行一次

  • function:它是这个函数的默认值,如果为此值,那么这个fixture在每个测试函数前运行一次 ,

以下先看一个示例, 这就是scope等于session级别的 。

接下来我们再看一个scope=function级别的,这是它的默认设置 ,所以可以不设置 ,代码依旧这段代码。  


@pytest.fixture(autouse=True)
def fixture_demo():print("每次运行用例前都执行下这个函数")# case1 : 输入正确的用户名和正确的密码进行登录
def test_login_success():print("1")expect_result = 0actual_result = login('admin','123456').get('code')assert expect_result == actual_result# case2 : 输入正确的用户名和错误的密码进行登录
def test_password_is_wrong():print("2")expect_reesult = 3actual_result = login('admin','1234567').get('code')assert expect_reesult == actual_result

 运行结果 :

说明 :scope=function时 ,它会每次在执行测试用例前都会自动先执行一次fixture_demo函数 ,因为这个测试用例是一个函数级别的 。总结为 :当scope=function时 ,它会每次在执行测试用例前执行一次 。

接下来我们再看一种scope=class的情况,具体看代码 ,

@pytest.fixture(scope='class',autouse=True)
def fixture_demo():print("在每个类前面只运行一次")class TestLogin():# case1 : 输入正确的用户名和正确的密码进行登录def test_login_success(self):print("1")expect_result = 0actual_result = login('admin','123456').get('code')assert expect_result == actual_result# case2 : 输入正确的用户名和错误的密码进行登录def test_password_is_wrong(self):print("2")expect_reesult = 3actual_result = login('admin','1234567').get('code')assert expect_reesult == actual_result

 运行结果 :

因为这里面只有一个类 ,所以只运行了一次 。

在上面我们介绍过 ,fixture可以搞定所有的seup和teardown的各种情况 ,上面我们介绍了10个函数 ,而fixture这一个函数就可以搞定 。让我们再次回顾下setup和teardown是啥意思 ,编写一个用例就知道了 。

  1. 编写一个函数 :setup() ,主要用来做用例执行前的初始化工作

  2. 编写一个函数 :teardown() , 主要用例做用例执行后的恢复操作 。

  3. 编写一个测试用例 :test_case() , 所要运行的测试用例 。

那么,它的运行顺序将是 :setup() -> testcase() ->teardown() .

如果使用fixture的话 ,这里就需要用到Python的一个关键字 :yield , 这个关键字非常重要 。不解释,直接上代码 。

@pytest.fixture(autouse=True)
def fixture_demo():print("每次运行用例前都执行下这个函数")yield print("每次运行用例后再此执行下这个函数")"""
说明 : 针对以上的fixture, 只是在中间加了个yield ,下面又加了一行代码 。
"""# case1 : 输入正确的用户名和正确的密码进行登录
def test_login_success():print("1")expect_result = 0actual_result = login('admin','123456').get('code')assert expect_result == actual_result# case2 : 输入正确的用户名和错误的密码进行登录
def test_password_is_wrong():print("2")expect_reesult = 3actual_result = login('admin','1234567').get('code')assert expect_reesult == actual_result

直接看运行结果 :

我们再回来看它的运行流程 :  

你看 ,通过fixture和yield就完美的解决了setup和teardown的问题 ,再加上scope值的控制 ,它就可以解决在不同层次上的初始化和清除操作 。比如你想实现个setup_class和teardown_class() . 你只需要在fixture将scope的值设置为class就可以了 。

看到这里 ,你是否会觉得开发pytest的人真是个天才 。

接下来,我们介绍fixture中最牛的功能,数据参数化 。

在介绍这个参数化之前 ,再回头看看前面的测试用例 ,编写的几条测试用例中 ,你是否发现它们有着高度的相似点 。比如:同样都有预期结果、实际结果以及两者的断言 。所不同的只是测试数据 ,这就为我们做数据参数化提供了条件 。

让我们再回顾下什么是数据参数化 :就是操作步骤相同 ,数据不同 ,那就可以通过编写一个用例 ,传入不同的数据就可以搞定 。

而fixture中的params就可以做数据参数化 ,让我们先来了解下这个参数 :

参数名 :params , 它的值主要接受的是列表 ,而列表中的值存放的就是测试数据 。先看个例子 :

import pytest@pytest.fixture(params=[1, 2, 3])
def fixture_demo(request): # 传入参数request 系统参数return request.param  # 取列表中获取单个值,默认的取值方式def test_number(fixture_demo):print("------->fixture_demo")assert fixture_demo != 3 # 断言fixture_demo不等于3if __name__ == '__main__':pytest.main("-q  test05_fixture_params.py")#执行结果:可以发现结果运行了三次
============================= test session starts =============================
collecting ... collected 3 itemstest05_fixture_params.py::test_number[1] PASSED                          [ 33%]------->fixture_demotest05_fixture_params.py::test_number[2] PASSED                          [ 66%]------->fixture_demotest05_fixture_params.py::test_number[3] FAILED                          [100%]------->fixture_demotest05_fixture_params.py:8 (test_number[3])
3 != 3Expected :3
Actual   :3

以上运行的结果是什么意思呢 ?列表中传入3个数 ,它就运行了3次 ;那如果传8个数呢 ,肯定也就会运行8次 。这里面有几个关键参数需要说明 :

  • request : 在fixture_demo中传入的参数名,这是fixture函数中的系统参数 ,你想要接收params列表里的值,这个参数必须传 ,也必须这样写,这是规则。

  • request param : 在fixture_demo中的返回值,这也是固定格式 ,必须这样写 ,用来返回params列表里的值 ,每次只返回列表里的一个值 。这也是为什么在测试用例中只调用了一次fixture_demo函数 ,而被运行了三次的原因 。

在这里要特别说明return 和 yeild的区别 。可以看到,在fixture_demo函数目前使用的返回语句关键字是return .但它是可以替换为yield的 ,唯一的不同 ,yield的运行流程较长,具体如下:

  1. 先运行yield之前的代码

  2. 接着返到测试用例中 ,将yeild后面的返回值 返回给测试用例 ,然后运行测试用例 。

  3. 接着再运行yeild下面的代码

如果是用return语句 ,它相当于只走了上面的第2步 ,不会有第1步和第3步 。

接下来,我们我们通过fixture来实现登录的测试用例 ,具体如下。

import pytest
from package_pytest.login import logincases = [(0,'admin','123456'),(3,'admin','1234567'),(2,'admin',''),(1,'','123456')]@pytest.fixture(params=cases)
def fixture_demo(request):print("===初始化方法===")yield request.param             # request.param :代表将params接受到的数据返回给测试用例中。print("===清除方法===")# case :运行登录测试用例
def test_login(fixture_demo):# fixture_demo : 每次循环进来以后,都给到一组数据 ,而这组数据其实就是一个元组print(fixture_demo)expect_result = fixture_demo[0]username = fixture_demo[1]password = fixture_demo[2]actual_result = login(username,password).get('code')assert expect_result == actual_result

运行结果 :

从上面我们可以看到它的运行逻辑为 :

  1. 将cases里的第一组数据传入到params参数中 。

  2. 然后fixture_demo通过request.param接受到这组数据 。

  3. 然后通过yield 将这组数据再返回到测试用例中,

  4. 测试用例通过参数名为:fixture_demo接受到这组数据 ,然后测试用例再进一步处理 。

  5. 接下来再返回到cases中获取第二组数据 ,然后依次循环以上的四个步骤 。

所以 ,当你列表中的数据是元组 ,那么在测试用例就以元组的形式去处理 ,就比如上面的这个案例就是保存的元组 。而如果列表的值是字符串,你在列表中就以字符串的方式进行处理 ,如果列表里的值是字典 ,那么在测试用例就以字典的形式来处理 。

而使用参数化进行测试用例实现时,在列表中存放的主要还是字典 。

参数名 :ids ,它是给生成的结fixture进行重命名 ,因为它之前生成的模式是按照fixture_demo[index] 进行命名的,其中这个index就是每次循环的数字 ,第一次为0 。如果这个不太好理解 ,就可以使用ids进行重命名 ,但是前提必须要有参数化:params。

 具体代码为 :

import pytest
from package_pytest.login import logincases = [(0,'admin','123456'),(3,'admin','1234567'),(2,'admin',''),(1,'','123456')]ids = ['正确的用户名和正确的密码登录','正确的用户名和错误的密码登录','空的密码登录','空的用户名登录']@pytest.fixture(params=cases,ids=ids)
def fixture_demo(request):print("===初始化方法===")yield request.param             # request.param :代表将params接受到的数据返回给测试用例中。print("===清除方法===")# case1 : 输入正确的用户名和正确的密码进行登录
def test_login(fixture_demo):# fixture_demo : 每次循环进来以后,都给到一组数据 ,而这组数据其实就是一个元组print(fixture_demo)expect_result = fixture_demo[0]username = fixture_demo[1]password = fixture_demo[2]actual_result = login(username,password).get('code')assert expect_result == actual_result

运行结果 :

参数名 :name ,它是给fixture函数重名名的,或者叫起别名 ,主要是为了调用时方便使用 ,一旦起别名,在测试用例中就必须的使用别名 。

import pytest
from package_pytest.login import logincases = [(0,'admin','123456'),(3,'admin','1234567'),(2,'admin',''),(1,'','123456')]@pytest.fixture(params=cases,name='fd')
def fixture_demo(request):print("===========初始化方法===============")yield request.param             # request.param :代表将params接受到的数据返回给测试用例中。print("============清除方法================")# case: 进行登录参数化
def test_login_success(fd):# fixture_demo : 每次循环进来以后,都给到一组数据 ,而这组数据其实就是一个元组print("1")expect_result = fd[0]username = fd[1]password = fd[2]actual_result = login(username,password).get('code')assert expect_result == actual_result

可以看到,给fixture_demo起了个别名为fd ,那么在测试用例中传入的fixture名字就必须是fd .

以上就是fixture装饰器的5个参数的介绍 ,其中scope 、autouse 、params这个三个是最主要的 ,把这三个参数用好即可 。

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

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

相关文章

16.linux计划任务管理

linux计划任务管理 文章目录 linux计划任务管理1. crond计划任务概述2. crond配置文件详解3. crond计划任务管理4. crond配置编写实例5. crond计划任务调试 1. crond计划任务概述 什么是计划任务,计划任务类似于我们平时生活中的闹钟。 在Linux系统的计划任务服务c…

判断是否是json字符串

一、在isJson.js文件里创建一个isJson类并抛出 /*** isJson 类用于判断一个字符串是否为有效的 JSON 字符串。* class isJson* param {string} str - 要判断的字符串。* returns {boolean} 如果字符串是有效的 JSON 字符串,则返回 true;否则返回 false。…

PPT插件-大珩助手-快速构建自己的图形

绘图板-快速构建自己的图形 通过手绘的方式,快速构建自己的想法和创意,通过在PPT中插入绘图,植入背景透明的绘图,点击画笔可切换橡皮擦,可以清空画板重新绘制。 素材库-存储图形 通过素材库存储自己的图形 图形调整…

操作系统期末考复盘

简答题4题*5 20分计算题2题*5 10分综合应用2题*10 20分程序填空1题10 10分 1、简答题(8抽4) 1、在计算机系统上配置OS的目标是什么?作用主要表现在哪个方面? 在计算机系统上配置OS,主要目标是实现:方便性、有…

如何把123转换成字符串的123

在许多编程语言中,将数字123转换为字符串的"123"是非常直接的。以下是几种常见编程语言的示例: Python num 123 str_num str(num) print(str_num) # 输出: 123 JavaScript let num 123; let str_num num.toString(); console…

【WebGIS实例】(12)MapboxGL解决叠加图层的点击事件冒泡

解决方法一: 监听有冲突的图层 map.on(click, layerName, e > {e.preventDefault()console.log(上面的图层)console.log(点击的要素, e.features[0]) })// 下面的那个图层:阻止默认事件,在下面的e可以看到_defaultPrevented: false&#…

three.js 学习笔记(学习中1.10更新) |

文章目录 three.js 学习笔记基础概念透视相机 第一个three.js应用threejs画布尺寸和布局canvas画布宽高度动态变化 坐标辅助器 THREE.AxesHelper实现动画效果requestAnimationFrame时间相关属性和方法 THREE.Clock类 相机控件 轨道控制器OrbitControls 灯光点光源点光源辅助观察…

17.SELinux

SELinux 文章目录 SELinux1、SELinux简介2、SELinux的模式3、管理SELinux上下文4、bool开关 1、SELinux简介 为了保障linux系统的安全,除了firewalld防火墙外,linux系统管理员通常会控制用户和组的权限,这种基于用户和组的安全模型称之为主动…

m1 + swoole(hyperf) + yasd + phpstorm 安装和debug

参考文档 Mac M1安装报错 checking for boost... configure: error: lib boost not found. Try: install boost library Issue #89 swoole/yasd GitHub 1.安装boost库 brew install boostbrew link boost 2.下载yasd git clone https://github.com/swoole/yasd.git 3.编…

Golang 中哪些类型可以作为 map 类型的 key?

目录 可以作为 map 键的类型 不能作为 map 键的类型 最佳实践 小结 在 Go 语言中,map 是一种内置的关联数据结构类型,由一组无序的键值对组成,每个键都是唯一的,并与一个对应的值相关联。本文将详细介绍哪些类型的变量可以作为…

轻量化神奇!看3D模型格式转换工具HOOPS Exchange如何轻松实现减面操作?

现在很多CAD模型都比较复杂,有时候为了一些特殊用途(轻量化显示、布尔运算、CAE网格剖分等),需要到对原始模型进行减面操作。在HOOPS Exchange中,就提供了对模型进行减面操作支持,以下内容就是HOOPS Exchan…

亚信安慧AntDB数据库容灾复制原理

AntDB数据库作为通信运营商领域的杰出的数据服务提供者,一直以来都十分重视数据安全问题,不断通过技术进步、方案创新等方式提升数据容灾能力。在信息化的时代,数据已经成为了重要的资源,对于企业来说,如何存储和管理这…

15.vdo管理

vdo管理 文章目录 vdo管理一、VDO基本概念二、常用操作三、验证VDO卷 一、VDO基本概念 VDO(Virtual Data Optimize虚拟数据优化) 通过压缩或删除存储设备上的数据来优化存储空间。VDO层放置在现有块存储设备例如RAID设备或本地磁盘的顶部。这些块设备也…

docker微服务案例

文章目录 建立简单的springboot项目(boot3)boot2建立通过dockerfile发布微服务部署到docker容器编写Dockerfile打包成镜像运行镜像微服务 建立简单的springboot项目(boot3) 1.建立module 2. 改pom <?xml version"1.0" encoding"UTF-8"?> <…

python爬虫实战(7)--获取it某家热榜

1. 需要的类库 import requests from bs4 import BeautifulSoup import pandas as pd2. 请求榜单 def fetch_ranking_data():url "https://m.xxx.com/rankm/" #某家response requests.get(url)if response.status_code 200:return response.contentelse:print(f…

需要登录的网站爬虫详解

概述 介绍一下请求状态原理分析需要登录的网站请求特点分析登陆前后请求差异如何从接口分析一步步构建一个合理的登录爬虫巧方法解决登录 案例分析 案例一 https://login2.scrape.center/ 默认重定向导致无法获取到重定向前的cookie 案例二爬虫源码 # codingutf-8import …

计算机网络期末复习(二)

物理层 解决&#xff1a;如何在连接各种计算机的传输媒体上传输比特流&#xff0c;而不是具体的传输媒体。 传输媒体&#xff1a;比如双绞线、同轴电缆、光纤等等。 主要任务&#xff1a;确定于传输媒体接口有关的一些特性。 传输媒体 导向形 非导向形 调制解调器 数字信号 和…

LeetCode [103] 二叉树的锯齿形层序遍历

Description: 给你二叉树的根节点 root &#xff0c;返回其节点值的 锯齿形层序遍历 。&#xff08;即先从左往右&#xff0c;再从右往左进行下一层遍历&#xff0c;以此类推&#xff0c;层与层之间交替进行&#xff09;。 解法&#xff1a;基本跟102类似&#xff0c;加了一个…

杰理AC63蓝牙名修改

杰理SDK会自动从VM区域读取蓝牙名字。代码如下&#xff0c;在user_cfg.c文件里。 //-----------------------------CFG_BT_NAME--------------------------------------//ret syscfg_read(CFG_BT_NAME, tmp, 32);if (ret < 0) {log_info("read bt name err\n");}…