python-pytest學習(五)-yield操作


一、前言

  fixture的teardown操作並不是獨立的函數,用yield關鍵字呼喚teardown操作。上一次實現了在每個用例之前執行初始化操作,那么用例執行完之后,如需要清除數據(或還原)操作,可以使用yield來實現。fixture通過scope參數控制setup級別,既然有setup作為用例之前的操作,用例執行完之后那肯定也有teardown操作。

  這里用到fixture的teardown操作並不是獨立的函數,用yield關鍵字呼喚teardown操作。fixture的teardown操作並不是獨立的函數,可以用yield關鍵字呼喚teardown操作。

  我們之前學習的都是測試用例的前置固件,也就是相當於“setup”。那自然就引申出一個問題“teardown”?這就是我們今天學習的yield和addfinalizer。

(1)yield

  yield是一個關鍵字,它不是單獨存在的,要寫在fixture標記的固件中。

  我們在聲明的固件myfixture中加入yield關鍵字,在它下面寫測試用例執行后想要運行的代碼;其他有關於固件的使用沒有任何差別。需要說明的一點是我們在pytest主函數中增加一個參數“-setup-show”,它會顯示出固件的執行情況。

  fixture里面的teardown用yield來喚醒teardown的執行。

  如果測試用例中的代碼出現異常或者斷言失敗,並不會影響他的固件中yield后的代碼執行;但是如果固件中的yield之前的代碼也是相當於setup部分的代碼,出現錯誤或斷言失敗,那么yield后的代碼將不會再執行,當然測試用例中的代碼也不會執行。

  我們也可以通過request.addfinalizer()的方式實現“teardown”。

  我們在固件中傳入request參數;又在固件中定義了一個內置函數;最后將定義的內置函數添加到request的addfinalize中。

 

二、scope="function"

  當pytest.fixture(scope="function")時,pytest的yield類似unittest的teardown。每個方法(函數)都會執行一次。

  1.新建test_function1.py文件

import pytest

@pytest.fixture(scope="function")
def login():
    print("登錄成功")
    yield
    print("用例執行完成,收尾")

def test1(login):
    print("操作1")
    print("---------------------------------------------------")

def test2(login):
    print("操作2")
    print("---------------------------------------------------")

def test3(login):
    print("操作3")
    print("---------------------------------------------------")

if __name__=="__main__":
    pytest.main(["-s","test_function1.py"])
    

 

運行結果:

  從結果看錯,雖然test1,test2,test3三個地方都調用了login函數,並且它會在每一個用例前執行一次。

  2.如果test1不調用,test2(調用login),test3不調用,運行順序會是怎樣的?

import pytest

@pytest.fixture(scope="function")
def login():
    print("登錄成功")
    yield
    print("用例執行完成,收尾")

def test1():
    print("操作1")
    print("---------------------------------------")

def test2(login):
    print("操作2")
    print("---------------------------------------")

def test3():
    print("操作3")
    print("---------------------------------------")

if __name__=="__main__":
    pytest.main(["-s","test_function2.py"])

 

運行結果:

 

 

   從結果看出,function級別的fixture在當前.py模塊里,只會在用例(test_s2)第一次調用前執行一次。

三、scope=“module”

  (1)fixture參數scope=“module”,module作用是整個.py文件都會生效(整個文件只會執行一次),用例調用時,參數寫上函數名稱就行。

import pytest

@pytest.fixture(scope="module")
def login():
    print("登錄成功")
    yield
    print("用例執行完成,收尾")

def test1(login):
    print("操作1")
    print("-------------------------------------------")

def test2(login):
    print("操作2")
    print("-------------------------------------------")

def test3(login):
    print("操作3")
    print("-------------------------------------------")

if __name__=="__main__":
    pytest.main(["-s","test_function3.py"])

運行結果:

 

 

   從結果看出,雖然test1,test2,test3三個地方都調用了login函數,但是它只會在第一個用例前執行一次。

  (2)如果test1,不調用,test2(調用login),test3不調用,運行順序會是怎樣的?

import pytest

@pytest.fixture(scope="module")
def login():
    print("登錄成功")
    yield
    print("用例執行完成,收尾")

def test1():
    print("操作1")
    print("-------------------------------------------")

def test2(login):
    print("操作2")
    print("-------------------------------------------")

def test3():
    print("操作3")
    print("-------------------------------------------")

if __name__=="__main__":
    pytest.main(["-s","test_function4.py"])

運行結果:

 

 

   從結果看出,module級別的fixture在當前.py模塊里,只會在用例(test_s2)第一次調用前執行一次。

 

四、yield執行teardown

  有個問題前面的代碼中有一個yield關鍵字是用來干什么的?其實是用來喚醒teardown。

  1.fixture里面的teardown用yield來喚醒teardown的執行

import pytest

@pytest.fixture(scope="module")
def login():
    print("登錄成功")
    yield
    print("執行teardown!")
    print("用例執行完成,收尾")

def test1(login):
    print("操作1")
    print("--------------------------------------")

def test2(login):
    print("操作2")
    print("--------------------------------------")

def test3(login):
    print("操作3")
    print("--------------------------------------")

if __name__=="__main__":
    pytest.main(["-s","test_function5.py"])
    

運行結果:

 

 

 

五、yield遇到異常

  1.如果其中一個用例出現異常,不影響yield后面的teardown執行,運行結果互不影響,並且在用例全部執行完之后,會喚醒teardown的內容。

import pytest

@pytest.fixture(scope="module")
def login():
    print("登錄成功")
    yield
    print("執行teardown!")
    print("用例執行完成,收尾")

def test1(login):
    print("操作1")
    print("----------------------------------")
    # 如果第一個用例異常了,不影響其他用例執行
    raise NameError # 模擬異常

def test2(login):
    print("操作2")
    print("----------------------------------")

def test3(login):
    print("操作3")
    print("----------------------------------")

if __name__=="__main__":
    pytest.main(["-s","test_function6.py"])
    

運行結果:

  2.如果在setup就異常了,那么是不會去執行yield后面的teardown內容了

  3.yield也可以配合with語句使用,以下是官方案例:

import smtplib
import pytest

@pytest.fixture(scope="module")
def smtp():
    with smtplib.SMTP("smtp.gmail.com") as smtp:
        yield smtp # provide the fixture value
        

 

六、addfinalizer終結函數

  1.除了yield可以實現teardown,在request-context對象中注冊addfinalizer方法也可以實現終結函數。

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 smpt_connection")
        smtp_connection.close()
    request.addfinalizer(fin)
    return smtp_connection # provide the fixture value

 

  2.yield和addfinalizer方法都是在測試完成后呼叫相應的代碼。但是addfinalizer不同的是:

  (1)它可以注冊多個終結函數;

  (2)這些終結方法總是會被執行,無論在之前的setup code有沒有拋出錯誤。這個方法對於正確關閉所有的fixture創建的資源非常便利,即使其一在創建或獲取時失敗。

 

參考文章:https://mp.weixin.qq.com/s/QcmjhOhWvdcMFL0sOpaiDA


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM