pytest进阶教程之fixture函数详解


Posted in Python onMarch 29, 2021

fixture函数存在意义

与python自带的unitest测试框架中的setup、teardown类似,pytest提供了fixture函数用以在测试执行前和执行后进行必要的准备和清理工作。但是相对来说又比setup、teardown好用。

firture相对于setup和teardown的优势

  1. 命名方式灵活,不局限于setup和teardown这几个命名
  2. conftest.py 配置里可以实现数据共享,不需要import就能自动找到一些配置
  3. scope="module" 可以实现多个.py跨文件共享前置, 每一个.py文件调用一次
  4. scope="session" 以实现多个.py跨文件使用一个session来完成多个用例

fixture函数定义

通过将fixture声明为参数名,测试用例函数可以请求fixture。fixture修饰器来标记固定的工厂函数,在其他函数,模块,类或整个工程调用它时会被激活并优先执行,通常会被用于完成预置处理和重复操作。

# 定义的夹具函数,使用装饰器pytest.fixture
@pytest.fixture
def my_fruit():
 print("login:用户执行登录操作")
 
# 使用夹具函数的测试用例
def test_my_fruit_in_basket(my_fruit):
 print("hello world")
 
if __name__ == '__main__':
 pytest.main(['test_login.py::test_my_fruit_in_basket', '-s'])
 
#执行结果:
collected 1 item
test_login.py login:
用户执行登录操作
hello world
.
============================== 1 passed in 0.02s ==========================

fixture作用

  • 做测试前后的初始化设置,如测试数据准备,链接数据库,打开浏览器等这些操作都可以使用fixture来实现。
  • 测试用例的前置条件可以使用fixture实现 。
  • 支持经典的xunit fixture ,像unittest使用的setup和teardown。
  • fixture可以实现unittest不能实现的功能,比如unittest中的测试用例和测试用例之间是无法传递参数和数据的,但是fixture却可以解决这个问题。

调用fixture有三种方式

Fixture名字作为测试用例的参数

可以直接使用fixture名称作为输入参数(是个典型的高阶函数),在这种情况下,fixture函数返回的fixture实例将被注入,最终在测试用例执行前执行这个装饰过的函数。如下列代码,①将返回值传递给测试用例,②通过函数入参方式,可以传入多个fixture函数

import pytest
 
@pytest.fixture
def first_entry():
 return "a"
@pytest.fixture
def order(first_entry):
 return [first_entry]
def test_string(order):
 order.append("b")
 assert order == ["a", "b"], "断言执行失败"
 
if __name__ == '__main__':
 pytest.main(['test_login.py::test_string', '-s'])

使用@pytest.mark.usefixtures('fixture')装饰器

每个函数或者类前使用@pytest.mark.usefixtures('fixture')装饰器进行装饰。

import pytest
@pytest.fixture
def my_fruit():
 print("login:用户执行登录操作")
 
# 被夹具函数装饰的测试用例
@pytest.mark.usefixtures("my_fruit")
def test_my_fruit_in_basket():
 print("hello world")
 
 
if __name__ == '__main__':
 pytest.main(['test_login.py', '-s', '-q'])
  
# 执行结果
login:用户执行登录操作 
hello world 
. 
1 passed in 0.01s

使用autouse参数

指定fixture的参数autouse=True这样模块内的每个测试用例会自动调用fixture。

import pytest
@pytest.fixture(autouse=True)
def my_fruit():
 print("login:用户执行登录操作")
 
# 被夹具函数装饰的测试用例
def test_my_fruit_in_basket():
 print("hello world")
 
 
if __name__ == '__main__':
 pytest.main(['test_login.py', '-s', '-q'])

备注: 如果fixture有返回值,那么usefixture以及autouse就无法获取到返回值,这个是装饰器usefixture与用例直接传fixture参数的区别。 因此最常用的是通过参数传递的方法。

指定Fixture函数的作用范围

Fixture中的scope的参数,控制Fixture函数的作用范围

scope = ‘function' 测试函数维度,默认范围,则在测试结束时销毁fixture。

scope = ‘class' 测试类维度,在class中最后一次测试的拆卸过程中,夹具被破坏。

scope = ‘module' 测试文件维度,在模块中最后一次测试的拆卸过程中,夹具被破坏。

scope = ‘session' 测试会话维度,夹具在测试会话结束时被销毁。

fixture函数的返回值:return 和 yield 和 addfinalizer终结函数

return:

通过下面的代码,我们已经发现可以通过测试用例函数传入参数的形式,直接使用fixture函数的返回值,这个相对来说比较简单。

import pytest
 
@pytest.fixture
def first_entry():
 return "a"
@pytest.fixture
def order(first_entry):
 return [first_entry]
def test_string(order):
 order.append("b")
 assert order == ["a", "b"], "断言执行失败"
  
if __name__ == '__main__':
 pytest.main(['test_login.py::test_string', '-s'])

yield:

yeild也是一种函数的返回值类型,是函数上下文管理器,使用yield被调fixture函数执行遇到yield会停止执行,接着执行调用的函数,调用的函数执行完后会继续执行fixture函数yield关键后面的代码。因此利用fixture函数,我们可以说pytest集合了setup、teardown,既做了初始化,又做了后置的清理工作。

import pytest
from emaillib import Email, MailAdminClient
 
@pytest.fixture
def mail_admin():
 return MailAdminClient()
 
# 配置发送者的fixture函数
@pytest.fixture
def sending_user(mail_admin):
 user = mail_admin.create_user() #setup:创建发件人
 yield user      # 返回发件人
 admin_client.delete_user(user) #teardown:删除发件人
 
# 配置收件人的fixture函数
@pytest.fixture
def receiving_user(mail_admin):
 user = mail_admin.create_user() #setup:创建收件人
 yield user      #返回收件人
 admin_client.delete_user(user) #teardown:删除收件人
 
def test_email_received(sending_user, receiving_user, email):
 email = Email(subject="Hey!", body="How's it going?")
 sending_user.send_email(email, receiving_user)
 assert email in receiving_user.inbox

项目中的实际使用

翻译下面代码,在调用Entry_into_index前,启动APP,遇到yield关键字,中止fixture函数调用,执行调用函数Entry_into_index内容,在Entry_into_index函数调用后,执行yield函数后的driver.close_app(),关闭APP。

@pytest.fixture(scope='session')
def startApp_fixture(start_app):
 driver = start_app
 res = lp(driver).get_agree_info()
 try:
  assert res == "同意"
 except Exception as e:
  log.error("启动APP失败")
  log.exception(e)
  raise e
 else:
  lp(driver).click_agree()
  lp(driver).click_next_step()
  lp(driver).click_alert()
  lp(driver).click_pass()
  # 创建首页
  index_page = indexPage(driver)
  yield index_page, driver
  # 后置条件
  time.sleep(3)
  driver.close_app()
   
# 调用fixture函数
@pytest.fixture(scope='session')
def Entry_into_index(startApp_fixture)
 index_page = startApp_fixture()[0]
 driver = startApp_fixture()[1]

fixture函数需要传递参数

工厂作为固定装置:可以使用闭包,通过外部去调用函数里面函数。

工厂固定装置原因:

上面已经说过,调用fixture函数A可以通过用fixture名称作为调用函数B参数,在这种情况下,fixture函数返回的fixture实例将被注入,最终在测试用例B执行前执行这个装饰过的函数def B(A):pass。但是有个问题在给测试用例添加装饰函数时,传入的参数是fixture函数的函数名,如果需要给fixture函数添加参数时,是不可以用下面形式,代码会直接报错。原因是测试用例传入参数为fixture函数名,如果fixture函数名添加(参数)后,表现形式为add(params)实际为函数调用。可参考高阶函数与装饰器,并无此用法。

pytest进阶教程之fixture函数详解

解决方式使用闭包,如下图代码:make_customer_record函数返回的是内部函数_make_customer_record(夹具不直接返回数据,而是返回一个生成数据的函数),注意此处未加(),非函数调用,因此在测试用例中customer_1 = make_customer_record("Lisa")此处可拆解为两部分,customer_1 = make_customer_record的结果为_make_customer_record对象 ,加上("Lisa") 实际是对调_make_customer_record函数进行调用:函数名+(参数),以达到可以传参的目的。

@pytest.fixture
def make_customer_record():
 def _make_customer_record(name):
  return {"name": name, "orders": []}
 
 return _make_customer_record #注意此处不加(),非函数调用
 
def test_customer_records(make_customer_record):
 customer_1 = make_customer_record("Lisa")

 

Python 相关文章推荐
Python实现希尔排序算法的原理与用法实例分析
Nov 23 Python
Python实现的朴素贝叶斯分类器示例
Jan 06 Python
numpy中实现二维数组按照某列、某行排序的方法
Apr 04 Python
深入了解Python枚举类型的相关知识
Jul 09 Python
使用Pyinstaller转换.py文件为.exe可执行程序过程详解
Aug 06 Python
Python开发之基于模板匹配的信用卡数字识别功能
Jan 13 Python
flask框架蓝图和子域名配置详解
Jan 25 Python
django使用F方法更新一个对象多个对象字段的实现
Mar 28 Python
keras 获取某层输出 获取复用层的多次输出实例
May 23 Python
python怎么判断素数
Jul 01 Python
Python 找出英文单词列表(list)中最长单词链
Dec 14 Python
如何Python使用re模块实现okenizer
Apr 30 Python
python中pandas.read_csv()函数的深入讲解
Mar 29 #Python
python编写函数注意事项总结
Mar 29 #Python
python基于tkinter制作无损音乐下载工具
Python requests库参数提交的注意事项总结
Python爬虫爬取全球疫情数据并存储到mysql数据库的步骤
Python爬虫数据的分类及json数据使用小结
Mar 29 #Python
python re模块和正则表达式
Mar 24 #Python
You might like
php设计模式 Decorator(装饰模式)
2011/06/26 PHP
仿Aspnetpager的一个PHP分页类代码 附源码下载
2012/10/08 PHP
基于MySQL到MongoDB简易对照表的详解
2013/06/03 PHP
PHP中array_slice函数用法实例详解
2014/11/25 PHP
php截取字符串函数分享
2015/02/02 PHP
PHP处理二进制数据的实现方法
2016/06/13 PHP
PHP sleep()函数, usleep()函数
2016/08/25 PHP
PHP页面输出时js设置input框的选中值
2016/09/30 PHP
php判断电子邮件是否正确方法
2018/12/04 PHP
XML+XSL 与 HTML 两种方案的结合
2007/04/22 Javascript
纯js实现重发验证码按钮倒数功能
2015/04/21 Javascript
JQuery中层次选择器用法实例详解
2015/05/18 Javascript
javascript函数命名的三种方式及区别介绍
2016/03/22 Javascript
详解Angular4中路由Router类的跳转navigate
2017/06/09 Javascript
深入浅析AngularJs模版与v-bind
2018/07/06 Javascript
手把手带你封装一个vue component第三方库
2019/02/14 Javascript
微信公众平台 客服接口发消息的实现代码(Java接口开发)
2019/04/17 Javascript
基于vue-cli搭建多模块且各模块独立打包的项目
2019/06/12 Javascript
vue实现吸顶、锚点和滚动高亮按钮效果
2019/10/21 Javascript
PHP读取远程txt文档到数组并实现遍历
2020/08/25 Javascript
Django中使用celery完成异步任务的示例代码
2018/01/23 Python
Python使用while循环花式打印乘法表
2019/01/28 Python
如何运行.ipynb文件的图文讲解
2019/06/27 Python
python实现飞机大战项目
2020/03/11 Python
python根据字典的键来删除元素的方法
2020/08/16 Python
阿根廷首家户外用品制造商和经销商:Montagne
2018/02/12 全球购物
Lowe’s加拿大:家居装修、翻新和五金店
2019/12/06 全球购物
Spartoo美国:欧洲排名第一的在线时装零售商
2019/12/12 全球购物
俄罗斯奢侈品牌衣服、鞋子和配饰的在线商店:INTERMODA
2020/07/17 全球购物
企业总经理职责
2014/02/02 职场文书
统计专业自荐书
2014/07/06 职场文书
小学学习委员竞选稿
2015/11/20 职场文书
Mysql 如何批量插入数据
2021/04/06 MySQL
SpringBoot+VUE实现数据表格的实战
2021/08/02 Java/Android
Nginx+Windows搭建域名访问环境的操作方法
2022/03/17 Servers
悬疑名作《朋友游戏》动画无字ED宣传片 新角色公开
2022/04/13 日漫