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采集百度百科的方法
Jun 05 Python
python字典DICT类型合并详解
Aug 17 Python
解决Django数据库makemigrations有变化但是migrate时未变动问题
May 30 Python
在PyCharm中三步完成PyPy解释器的配置的方法
Oct 29 Python
python-opencv颜色提取分割方法
Dec 08 Python
Python+Selenium使用Page Object实现页面自动化测试
Jul 14 Python
keras .h5转移动端的.tflite文件实现方式
May 25 Python
Python类super()及私有属性原理解析
Jun 15 Python
Python 爬虫的原理
Jul 30 Python
记一次Django响应超慢的解决过程
Sep 17 Python
python 爬虫如何正确的使用cookie
Oct 27 Python
python实现socket简单通信的示例代码
Apr 13 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数组函数序列之array_unique() - 去除数组中重复的元素值
2011/10/29 PHP
php判断类是否存在函数class_exists用法分析
2014/11/14 PHP
smarty模板引擎之配置文件数据和保留数据
2015/03/30 PHP
PHP入门教程之上传文件实例详解
2016/09/11 PHP
PHP获取用户客户端真实IP的解决方案
2016/10/10 PHP
详解php语言最牛掰的Laravel框架
2017/11/20 PHP
设定php简写功能的方法
2019/11/28 PHP
动态调用css文件——jquery的应用
2007/02/20 Javascript
jquery 学习笔记一
2010/04/07 Javascript
关于JavaScript的面向对象和继承有利新手学习
2013/01/11 Javascript
js切换光标示例代码
2013/10/10 Javascript
node.js中的buffer.write方法使用说明
2014/12/10 Javascript
jQuery复制表单元素附源码分享效果演示
2015/09/30 Javascript
常常会用到的截取字符串substr()、substring()、slice()方法详解
2015/12/16 Javascript
jquery div模态窗口的简单实例
2016/05/28 Javascript
Highcharts学习之数据列
2016/08/03 Javascript
Vue.js动态添加、删除选题的实例代码
2016/09/30 Javascript
javascript获取图片的top N主色值方法详解
2018/01/26 Javascript
vue页面加载闪烁问题的解决方法
2018/03/28 Javascript
react 中父组件与子组件双向绑定问题
2019/05/20 Javascript
JavaScript函数式编程(Functional Programming)高阶函数(Higher order functions)用法分析
2019/05/22 Javascript
使用layui日期控件laydate对开始和结束时间进行联动控制的方法
2019/09/06 Javascript
JS如何在不同平台实现多语言方式
2020/07/16 Javascript
Python RuntimeError: thread.__init__() not called解决方法
2015/04/28 Python
Python实用技巧之列表、字典、集合中根据条件筛选数据详解
2018/07/11 Python
使用matplotlib中scatter方法画散点图
2019/03/19 Python
Ubuntu20下的Django安装的方法步骤
2021/01/24 Python
95%的面试官都会问到的50道Java线程题,附答案
2012/08/03 面试题
《胖乎乎的小手》教学反思
2014/02/26 职场文书
文化产业实施方案
2014/06/07 职场文书
亲子阅读的活动方案
2014/08/15 职场文书
2014年小学教师工作自我评价
2014/09/22 职场文书
机关干部作风建设剖析材料
2014/10/23 职场文书
军事博物馆观后感
2015/06/05 职场文书
国际贸易实训总结
2015/08/03 职场文书
七年级上册生物的课件
2019/08/07 职场文书