当咱们运行测试函数时,咱们但愿确保测试函数在运行结束后,能够本身清理掉对环境的影响。
这样的话,它们就不会干扰任何其余的测试函数,更不会日积月累的留下愈来愈多的测试数据。html
用过unittest的朋友相信都知道teardown这个函数,作的是同样的事情,那么下面姑且就把这种“善后”工做的代码
叫作teardown代码吧。session
而pytest中的fixture,也提供了这样一个很是有用的系统,咱们能够在里面定义teardown代码。函数
这里可使用2种方式来实现,分别是yield
和addfinalizer
测试
在有yield
的fixtures函数中,关键字yield
能够代替 return
,能够把fixture里的一些对象传递给调用它们的fixture函数或者测试函数。
就像其余普通的fixture函数同样。区别仅仅是:url
yield
替换掉了return
yield
以后pytest在执行fixture函数时,会根据fixture函数之间的线性关系顺序调用的。
可是,当测试函数运行结束的时候,pytest又会按照以前的顺序反方向来执行fixture中yield以后的代码。
结合示例看下,这里没有引用官方示例了,手写一个直观些的:code
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,经过前面几章的接触,
相信你们这时候已经能够梳理出先后调用顺序了:orm
因此,fixture函数的前后顺序是:fixture_one
、fixture_two
、fixture_adding
。
那么,能够得知测试结束后的teardown代码执行顺序:fixture_adding
、fixture_two
。htm
运行一下代码,验证下结果是否符合咱们的梳理:对象
============================= 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结构应该是什么样子。这个官方文档另起进行说明,这里一样。
在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, 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 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
文档中也有另外的讲解,届时再分享。
上方代码是一个终结函数,若是要注册多个呢?
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, 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 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
这里要注意的是,多个终结器的状况下,执行的顺序是与注册时候相反的。
目前从官方文档中看到的是
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了,这里算是埋下一个坑,待后续解决。