【pytest官方文檔】解讀fixtures - 7. Teardown處理,yield和addfinalizer

當咱們運行測試函數時,咱們但願確保測試函數在運行結束後,能夠本身清理掉對環境的影響。
這樣的話,它們就不會干擾任何其餘的測試函數,更不會日積月累的留下愈來愈多的測試數據。html

用過unittest的朋友相信都知道teardown這個函數,作的是同樣的事情,那麼下面姑且就把這種「善後」工做的代碼
叫作teardown代碼吧。session

而pytest中的fixture,也提供了這樣一個很是有用的系統,咱們能夠在裏面定義teardown代碼。函數

這裏可使用2種方式來實現,分別是yieldaddfinalizer測試

1、yield fixtures(推薦)

1, yield 和 return

在有yield的fixtures函數中,關鍵字yield 能夠代替 return,能夠把fixture裏的一些對象傳遞給調用它們的fixture函數或者測試函數。
就像其餘普通的fixture函數同樣。區別僅僅是:url

  1. yield替換掉了return
  2. teardown代碼放置在yield以後

2, 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

  1. test_demo 測試函數,先去調用fixture函數 fixture_two,而後調用 fixture_adding。
  2. 在fixture函數 fixture_two中,又會去調用另外一個fixture函數 fixture_one。
  3. 在fixture函數 fixture_adding中,調用了 fixture_one、fixture_two。

因此,fixture函數的前後順序是:fixture_onefixture_twofixture_adding
那麼,能夠得知測試結束後的teardown代碼執行順序:fixture_addingfixture_twohtm

運行一下代碼,驗證下結果是否符合咱們的梳理:對象

============================= 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結構應該是什麼樣子。這個官方文檔另起進行說明,這裏一樣。

2、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, 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文檔中也有另外的講解,屆時再分享。

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, 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

這裏要注意的是,多個終結器的狀況下,執行的順序是與註冊時候相反的

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了,這裏算是埋下一個坑,待後續解決。

相關文章
相關標籤/搜索