當咱們運行測試函數時,咱們但願確保測試函數在運行結束後,能夠本身清理掉對環境的影響。
這樣的話,它們就不會干擾任何其餘的測試函數,更不會日積月累的留下愈來愈多的測試數據。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了,這裏算是埋下一個坑,待後續解決。