目录
一、Fixture之conftest.py
1、Fixture优势
2、fixture参数传入(scope=”function”)
3、conftest.py配置
二、Fixture之yield实现teardown
1、scope=“module”
2、yield执行teardown
3、yield遇到异常
4、addfinalizer终结函数
一、Fixture之conftest.py
1、Fixture优势
前面一篇讲到用例加setup和teardown可以实现在测试用例之前或之后加入一些操作,但这种是整个脚本全局生效的
如果我想实现以下场景:
用例1需要先登录,用例2不需要登录,用例3需要先登录。很显然这就无法用setup和teardown来实现了。这就是学习的目的,自定义测试用例的预置条件
firture相对于setup和teardown来说应该有以下几点优势
1.命名方式灵活,不局限于setup和teardown这几个命名
2.conftest.py 配置里可以实现数据共享,不需要import就能自动找到一些配置
3.scope=”module” 可以实现多个.py跨文件共享前置
4.scope=”session” 以实现多个.py跨文件使用一个session来完成多个用例
fixture(scope="function", params=None, autouse=False, ids=None, name=None):
"""使用装饰器标记fixture的功能
'''程序员曦曦'''
可以使用此装饰器(带或不带参数)来定义fixture功能。 fixture功能的名称可以在以后使用
引用它会在运行测试之前调用它:test模块或类可以使用pytest.mark.usefixtures(fixturename标记。
测试功能可以直接使用fixture名称作为输入参数,在这种情况下,夹具实例从fixture返回功能将被注入。"""
:arg scope: scope 有四个级别参数 "function" (默认), "class", "module" or "session".
:arg params: 一个可选的参数列表,它将导致多个参数调用fixture功能和所有测试使用它
:arg autouse: 如果为True,则为所有测试激活fixture func 可以看到它。 如果为False(默认值)则显式需要参考来激活fixture
:arg ids: 每个字符串id的列表,每个字符串对应于params 这样他们就是测试ID的一部分。 如果没有提供ID它们将从params自动生成
:arg name: fixture的名称。 这默认为装饰函数的名称。 如果fixture在定义它的同一模块中使用,夹具的功能名称将被请求夹具的功能arg遮蔽; 解决这个问题的一种方法是将装饰函数命名
"fixture_ <fixturename>"然后使用"@ pytest.fixture(name ='<fixturename>')""。
Fixtures可以选择使用yield语句为测试函数提供它们的值,而不是return。 在这种情况下,yield语句之后的代码块作为拆卸代码执行,而不管测试结果如何。fixture功能必须只产生一次
2、fixture参数传入(scope=”function”)
1.实现场景:用例1需要先登录,用例2不需要登录,用例3需要先登录
# 新建一个文件test_fixt.py
# coding:utf-8
'''程序员曦曦'''
import pytest
# 不带参数时默认scope="function"
@pytest.fixture()
def login():
print("输入账号,密码先登录")
def test_s1(login):
print("用例1:登录之后其它动作111")
def test_s2(): # 不传login
print("用例2:不需要登录,操作222")
def test_s3(login):
print("用例3:登录之后其它动作333")
if __name__ == "__main__":
pytest.main(["-s", "test_fix.py"])
运行结果:
============================= test session starts =============================
platform win32 -- Python 3.6.0, pytest-3.6.3, py-1.5.4, pluggy-0.6.0
rootdir: E:\MOMO, inifile:
collected 3 items
test_fix.py 输入账号,密码先登录
用例1:登录之后其它动作111
.用例2:不需要登录,操作222
.输入账号,密码先登录
用例3:登录之后其它动作333
.
========================== 3 passed in 0.06 seconds ===========================
2.如果@pytest.fixture()里面没有参数,那么默认scope=“function”,也就是此时的级别的function,针对函数有效
3、conftest.py配置
1.上面一个案例是在同一个.py文件中,多个用例调用一个登陆功能,如果有多个.py的文件都需要调用这个登陆功能的话,那就不能把登陆写到用例里面去了
此时应该要有一个配置文件,单独管理一些预置的操作场景,pytest里面默认读取conftest.py里面的配置
conftest.py配置需要注意以下点:
-------conftest.py配置脚本名称是固定的,不能改名称
-------conftest.py与运行的用例要在同一个pakage下,并且有init.py文件
-------不需要import导入 conftest.py,pytest用例会自动查找
2.参考脚本代码设计如下
'''程序员曦曦'''
__init__.py
conftest.py
# coding:utf-8
import pytest
@pytest.fixture()
def login():
print("输入账号,密码先登录")
test_fix1.py
# coding:utf-8
import pytest
def test_s1(login):
print("用例1:登录之后其它动作111")
def test_s2(): # 不传login
print("用例2:不需要登录,操作222")
def test_s3(login):
print("用例3:登录之后其它动作333")
if __name__ == "__main__":
pytest.main(["-s", "test_fix1.py"])
test_fix2.py
# coding:utf-8
import pytest
def test_s4(login):
print("用例4:登录之后其它动作111")
def test_s5(): # 不传login
print("用例5:不需要登录,操作222")
if __name__ == "__main__":
pytest.main(["-s", "test_fix2.py"])
3.单独运行test_fix1.py和test_fix2.py都能调用到login()方法,这样就能实现一些公共的操作可以单独拿出来了
二、Fixture之yield实现teardown
1、scope=“module”
既然有setup作为用例之前前的操作,用例执行完之后那肯定也有teardown操作
这里用到fixture的teardown操作并不是独立的函数,用yield关键字呼唤teardown操作
1.fixture参数scope=”module”,module作用是整个.py文件都会生效,用例调用时,参数写上函数名称就行
# 新建一个文件test_f1.py
# coding:utf-8
import pytest
'''程序员曦曦'''
@pytest.fixture(scope="module")
def open():
print("打开浏览器,并且打开百度首页")
def test_s1(open):
print("用例1:搜索python-1")
def test_s2(open): # 不传login
print("用例2:搜索python-2")
def test_s3(open):
print("用例3:搜索python-3")
if __name__ == "__main__":
pytest.main(["-s", "test_f1.py"])
运行结果:
============================= test session starts =============================
platform win32 -- Python 3.6.0, pytest-3.6.3, py-1.5.4, pluggy-0.6.0
rootdir: D:\, inifile:
collected 3 items
..\..\..\..\..\..\MOMO\test_f1.py 打开浏览器,并且打开百度首页
用例1:搜索python-1
.用例2:搜索python-2
.用例3:搜索python-3
.
========================== 3 passed in 0.01 seconds ===========================
从结果看出,虽然test_s1,test_s2,test_s3三个地方都调用了open函数,但是它只会在第一个用例前执行一次
2.如果test_s1不调用,test_s2(调用open),test_s3不调用,运行顺序会是怎样的?
# 新建一个文件test_f1.py
# coding:utf-8
import pytest
''程序员曦曦'''
@pytest.fixture(scope="module")
def open():
print("打开浏览器,并且打开百度首页")
def test_s1():
print("用例1:搜索python-1")
def test_s2(open): # 不传login
print("用例2:搜索python-2")
def test_s3():
print("用例3:搜索python-3")
if __name__ == "__main__":
pytest.main(["-s", "test_f1.py"])
运行结果:
============================= test session starts =============================
platform win32 -- Python 3.6.0, pytest-3.6.3, py-1.5.4, pluggy-0.6.0
rootdir: D:\, inifile:
collected 3 items
..\..\..\..\..\..\MOMO\test_f1.py 用例1:搜索python-1
.打开浏览器,并且打开百度首页
用例2:搜索python-2
.用例3:搜索python-3
.
========================== 3 passed in 0.01 seconds ===========================
从结果看出,module级别的fixture在当前.py模块里,只会在用例(test_s2)第一次调用前执行一次
2、yield执行teardown
1.前面讲的是在用例前加前置条件,相当于setup,既然有setup那就有teardown,fixture里面的teardown用yield来唤醒teardown的执行
# 新建一个文件test_f1.py
# coding:utf-8
import pytest
'''程序员曦曦'''
@pytest.fixture(scope="module")
def open():
print("打开浏览器,并且打开百度首页")
yield
print("执行teardown!")
print("最后关闭浏览器")
def test_s1(open):
print("用例1:搜索python-1")
def test_s2(open): # 不传login
print("用例2:搜索python-2")
def test_s3(open):
print("用例3:搜索python-3")
if __name__ == "__main__":
pytest.main(["-s", "test_f1.py"])
运行结果:
============================= test session starts =============================
platform win32 -- Python 3.6.0, pytest-3.6.3, py-1.5.4, pluggy-0.6.0
rootdir: D:\, inifile:
collected 3 items
..\..\..\..\..\..\MOMO\test_f1.py 打开浏览器,并且打开百度首页
用例1:搜索python-1
.用例2:搜索python-2
.用例3:搜索python-3
.执行teardown!
最后关闭浏览器
========================== 3 passed in 0.01 seconds ===========================
3、yield遇到异常
1.如果其中一个用例出现异常,不影响yield后面的teardown执行,运行结果互不影响,并且在用例全部执行完之后,会呼唤teardown的内容
# 新建一个文件test_f1.py
# coding:utf-8
import pytest
'''程序员曦曦'''
@pytest.fixture(scope="module")
def open():
print("打开浏览器,并且打开百度首页")
yield
print("执行teardown!")
print("最后关闭浏览器")
def test_s1(open):
print("用例1:搜索python-1")
# 如果第一个用例异常了,不影响其他的用例执行
raise NameError # 模拟异常
def test_s2(open): # 不传login
print("用例2:搜索python-2")
def test_s3(open):
print("用例3:搜索python-3")
if __name__ == "__main__":
pytest.main(["-s", "test_f1.py"])
运行结果:
\MOMO\test_f1.py 打开浏览器,并且打开百度首页
用例1:搜索python-1
F
open = None
def test_s1(open):
print("用例1:搜索python-1")
# 如果第一个用例异常了,不影响其他的用例执行
> raise NameError # 模拟异常
E NameError
D:\MOMO\test_f1.py:16: NameError
用例2:搜索python-2
.用例3:搜索python-3
.执行teardown!
最后关闭浏览器
2.如果在setup就异常了,那么是不会去执行yield后面的teardown内容了
3.yield也可以配合with语句使用,以下是官方文档给的案例
# 官方文档案例
# content of test_yield2.py
import smtplib
import pytest
'''程序员曦曦'''
@pytest.fixture(scope="module")
def smtp():
with smtplib.SMTP("smtp.gmail.com") as smtp:
yield smtp # provide the fixture value
4、addfinalizer终结函数
1.除了yield可以实现teardown,在request-context对象中注册addfinalizer方法也可以实现终结函数
# 官方案例
# content of conftest.py
import smtplib
import pytest
@pytest.fixture(scope="module")
def smtp_connection(request):
smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
def fin():
print("teardown smtp_connection")
smtp_connection.close()
request.addfinalizer(fin)
return smtp_connection # provide the fixture value
2.yield和addfinalizer方法都是在测试完成后呼叫相应的代码。但是addfinalizer不同的是:
-------他可以注册多个终结函数
-------这些终结方法总是会被执行,无论在之前的setup code有没有抛出错误。这个方法对于正确关闭所有的fixture创建的资源非常便利,即使其一在创建或获取时失败。
【下面是我整理的2023年最全的软件测试工程师学习知识架构体系图】
一、Python编程入门到精通
二、接口自动化项目实战
三、Web自动化项目实战
四、App自动化项目实战
五、一线大厂简历
六、测试开发DevOps体系
七、常用自动化测试工具
八、JMeter性能测试
九、总结(尾部小惊喜)
生命不息,奋斗不止。每一份努力都不会被辜负,只要坚持不懈,终究会有回报。珍惜时间,追求梦想。不忘初心,砥砺前行。你的未来,由你掌握!
生命短暂,时间宝贵,我们无法预知未来会发生什么,但我们可以掌握当下。珍惜每一天,努力奋斗,让自己变得更加强大和优秀。坚定信念,执着追求,成功终将属于你!
只有不断地挑战自己,才能不断地超越自己。坚持追求梦想,勇敢前行,你就会发现奋斗的过程是如此美好而值得。相信自己,你一定可以做到!
资料获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!以上均可以分享,点下方小卡片进群领取。
【软件测试技术交流(资料分享)】:320231853(备注C)http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=eU1h9ykztGeBOGy_wSKn0v33ThZlM8Xh&authKey=3aLC55z%2F7ZglBlNu5uwQopOR2WZAlAuFDvJ6sqEnXLJP8y9fvuhBS0p%2FNOTaZyQG&noverify=0&group_code=320231853