当我们运行测试函数时,我们希望确保测试函数在运行结束后,可以自己清理掉对环境的影响。
这样的话,它们就不会干扰任何其他的测试函数,更不会日积月累的留下越来越多的测试数据。
用过unittest的朋友相信都知道teardown这个函数,做的是一样的事情,那么下面姑且就把这种“善后”工作的代码
叫做teardown代码吧。
而pytest中的fixture,也提供了这样一个非常有用的系统,我们可以在里面定义teardown代码。
这里可以使用2种方式来实现,分别是yield
和addfinalizer
一、yield fixtures(推荐)
1,yield 和 return
在有yield
的fixtures函数中,关键字yield
可以代替 return
,可以把fixture里的一些对象传递给调用它们的fixture函数或者测试函数。
就像其他普通的fixture函数一样。区别仅仅是:
-
yield
替换掉了return
- teardown代码放置在
yield
之后
2,yield的执行顺序
pytest在执行fixture函数时,会根据fixture函数之间的线性关系顺序调用的。
但是,当测试函数运行结束的时候,pytest又会按照之前的顺序反方向来执行fixture中yield之后的代码。
结合示例看下,这里没有引用官方示例了,手写一个直观些的:
import pytest
@pytest.fixture
def fixture_one():
print("\n执行fixture_one")
return 1
@pytest.fixture
def fixture_two(fixture_one):
print("\n执行fixture_two")
yield 2
print("\n执行fixture_two的teardown代码")
@pytest.fixture
def fixture_adding(fixture_one,fixture_two):
print("\n执行fixture_adding")
result = fixture_one + fixture_two
yield result
print("\n执行fixture_adding的teardown代码")
def test_demo(fixture_two,fixture_adding):
print("\n执行测试函数test_demo")
assert fixture_adding == 3
代码中,fixture中调用多个fixture,测试函数中调用多个fixture,通过前面几章的接触,
相信大家这时候已经可以梳理出前后调用顺序了:
- test_demo 测试函数,先去调用fixture函数 fixture_two,然后调用 fixture_adding。
- 在fixture函数 fixture_two中,又会去调用另一个fixture函数 fixture_one。
- 在fixture函数 fixture_adding中,调用了 fixture_one、fixture_two。
所以,fixture函数的先后顺序是:fixture_one
、fixture_two
、fixture_adding
。
那么,可以得知测试结束后的teardown代码执行顺序:fixture_adding
、fixture_two
。
运行一下代码,验证下结果是否符合我们的梳理:
============================= test session starts =============================
platform win32 -- Python 3.6.8,pytest-5.4.3,py-1.9.0,pluggy-0.13.1
rootdir: D:\练习\demo_fixture
plugins: allure-pytest-2.8.32,celery-4.3.0,Faker-4.14.2,base-url-1.4.2,html-2.1.1,metadata-1.10.0collected 1 item
test_module.py
执行fixture_one
执行fixture_two
执行fixture_adding
.
执行测试函数test_demo
执行fixture_adding的teardown代码
执行fixture_two的teardown代码
[100%]
============================== 1 passed in 0.09s ==============================
结果与我们刚才梳理的一致。
但是,值得注意的是,就算是teardown的代码是按照正确的顺序执行,也不能保证代码能正常执行的。
比如说teardown里的某些代码执行异常了,导致别的清理动作也没法执行。
这里就涉及到另一个点了:健壮的fixture结构应该是什么样子。这个官方文档另起进行说明,这里同样。
二、addfinalizer
1.request.addfinalizer把函数变成终结器
在pytest中想要做teardown的处理,除了使用带有yield的fixture函数,还可以直接添加终结器。
直接来看示例代码:
import pytest
@pytest.fixture()
def demo_fixture(request):
print("\n这个fixture在每个case前执行一次")
def demo_finalizer():
print("\n在每个case完成后执行的teardown")
#注册demo_finalizer为终结函数
request.addfinalizer(demo_finalizer)
def test_01(demo_fixture):
print("\n===执行了case: test_01===")
def test_02(demo_fixture):
print("\n===执行了case: test_02===")
看下运行结果:
============================= test session starts =============================
platform win32 -- Python 3.6.8,metadata-1.10.0collected 2 items
test_module.py
这个fixture在每个case前执行一次
.
===执行了case: test_01===
在每个case完成后执行的teardown
这个fixture在每个case前执行一次
.
===执行了case: test_02===
在每个case完成后执行的teardown
[100%]
============================== 2 passed in 0.10s ==============================
Process finished with exit code 0
运行结果可以看出,效果与yield是一致的。这算是一个固定写法,关于request
文档中也有另外的讲解,届时再分享。
2.request.addfinalizer注册多个终结器函数
上方代码是一个终结函数,如果要注册多个呢?
import pytest
@pytest.fixture()
def demo_fixture(request):
print("\n这个fixture在每个case前执行一次")
def demo_finalizer():
print("\n在每个case完成后执行的teardown")
def demo_finalizer2():
print("\n在每个case完成后执行的teardown2")
#注册demo_finalizer为终结函数
request.addfinalizer(demo_finalizer)
request.addfinalizer(demo_finalizer2)
def test_01(demo_fixture):
print("\n===执行了case: test_01===")
def test_02(demo_fixture):
print("\n===执行了case: test_02===")
if __name__ == '__main__':
pytest.main(['-s','test_module.py'])
运行结果:
============================= test session starts =============================
platform win32 -- Python 3.6.8,metadata-1.10.0collected 2 items
test_module.py
这个fixture在每个case前执行一次
.
===执行了case: test_01===
在每个case完成后执行的teardown2
在每个case完成后执行的teardown
这个fixture在每个case前执行一次
.
===执行了case: test_02===
在每个case完成后执行的teardown2
在每个case完成后执行的teardown
[100%]
============================== 2 passed in 0.09s ==============================
Process finished with exit code 0
这里要注意的是,多个终结器的情况下,执行的顺序是与注册时候相反的。
3.yield和addfinalizer的区别
目前从官方文档中看到的是
We have to be careful though,because pytest will run that finalizer once it’s been added,even if that fixture raises an exception after adding the finalizer.
一旦添加了终结器,pytest便会执行。
但是,当我尝试在setup代码中进行抛错,终结器的代码却并没有执行。
尝试搜索外网暂时也没得到有效的帮助,只能在GitHub上向pytest提了issue了,这里算是埋下一个坑,待后续解决。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。