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之使用Python操作数据库(1)
Nov 25 Python
Python中的面向对象编程详解(下)
Apr 13 Python
浅谈Python peewee 使用经验
Oct 20 Python
python中利用zfill方法自动给数字前面补0
Apr 10 Python
Django中反向生成models.py的实例讲解
May 30 Python
详解Django中间件的5种自定义方法
Jul 26 Python
Python 串口读写的实现方法
Jun 12 Python
python lambda表达式在sort函数中的使用详解
Aug 28 Python
Python自动采集微信联系人的实现示例
Feb 28 Python
解决Python在导入文件时的FileNotFoundError问题
Apr 10 Python
tensorflow基于CNN实战mnist手写识别(小白必看)
Jul 20 Python
python 实现压缩和解压缩的示例
Sep 22 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漏洞全解(详细介绍)
2012/11/13 PHP
简单的php文件上传(实例)
2013/10/27 PHP
PHP 返回数组后处理方法(开户成功后弹窗提示)
2017/07/03 PHP
JavaScript和CSS通过expression实现Table居中显示
2013/06/28 Javascript
浅析js中的浮点型运算问题
2014/01/06 Javascript
jQuery实现单击按钮遮罩弹出对话框(仿天猫的删除对话框)
2014/04/10 Javascript
javascript限制文本框输入值类型的方法
2015/05/07 Javascript
JS实现仿QQ效果的三级竖向菜单
2015/09/25 Javascript
javascript中call,apply,bind函数用法示例
2016/12/19 Javascript
关于AngularJs数据的本地存储详解
2017/01/20 Javascript
jQuery树控件zTree使用方法详解(一)
2017/02/28 Javascript
angularjs封装$http为factory的方法
2017/05/18 Javascript
总结js中的一些兼容性易错的问题
2017/12/18 Javascript
关于layui表单中按钮自动提交的解决方法
2019/09/09 Javascript
createObjectURL方法实现本地图片预览
2019/09/30 Javascript
vue-router之实现导航切换过渡动画效果
2019/10/31 Javascript
js实现选项卡效果
2020/03/07 Javascript
在Mac OS上使用mod_wsgi连接Python与Apache服务器
2015/12/24 Python
Python cookbook(数据结构与算法)将多个映射合并为单个映射的方法
2018/04/19 Python
Python语言检测模块langid和langdetect的使用实例
2019/02/19 Python
基于python的ini配置文件操作工具类
2019/04/24 Python
Python进程间通信Queue消息队列用法分析
2019/05/22 Python
Pandas_cum累积计算和rolling滚动计算的用法详解
2019/07/04 Python
全网最全python库selenium自动化使用详细教程
2021/01/12 Python
婴儿鞋,独特的婴儿服装和配件:Zutano
2018/11/03 全球购物
Booking.com缤客中国:全球酒店在线预订网站
2020/05/03 全球购物
幼儿园毕业园长感言
2014/02/24 职场文书
关于读书的演讲稿
2014/05/07 职场文书
改作风抓落实促发展心得体会
2014/09/10 职场文书
学生逃课检讨书1000字
2014/10/20 职场文书
二手车交易协议书标准版
2014/11/16 职场文书
房产公证书格式
2015/01/26 职场文书
2016年清明节红领巾广播稿
2015/12/17 职场文书
晚会开幕词范文
2016/03/04 职场文书
HTML中的表单Form实现居中效果
2021/05/25 HTML / CSS
SQL实现LeetCode(180.连续的数字)
2021/08/04 MySQL