1. 簡介
上一篇中,我們剛剛實現了在每個用例之前執行初始化操作,那么用例執行完之后如需要清除數據(或還原)操作,可以使用 yield 來實現。fixture通過scope參數控制setup級別,既然有setup作為用例之前前的操作,用例執行完之后那肯定也有teardown操作。
這里用到fixture的teardown操作并不是獨立的函數,用yield關鍵字呼喚teardown操作。fixture的teardown操作并不是獨立的函數,可以用yield關鍵字呼喚teardown操作。
我們之前學習的都是測試用例的前置固件,也就是相當于“setup”。說到這,細心的你可能想到了,那有沒有什么方式可以表示出“teardown”?這就是我們今天學習的yield和addfinalizer。
yield
yield是一個關鍵字,它不是單獨存在的,要寫在fixtrue標記的固件中。
我們在聲明的固件myfixture中加入yield關鍵字,在它下面寫測試用例執行后想要運行的代碼;其他有關于固件的使用沒有任何差別。需要說明的一點是我們在pytest主函數中增加了一個參數“–setup-show”,他會顯示出固件的執行情況。
fixture里面的teardown用yield來喚醒teardown的執行
如果測試用例中的代碼出現異常或者斷言失敗,并不會影響他的固件中yield后的代碼執行;但是如果固件中的yield之前的代碼也就是相當于setup部分的帶代碼,出現錯誤或斷言失敗,那么yield后的代碼將不會再執行,當然測試用例中的代碼也不會執行。
我們也可以通過request.addfinalizer()的方式實現“teardown”
我們在固件中傳入request參數;又在固件中定義了一個內置函數;最后將定義的內置函數添加到request的addfinalizer中。
2. scope="function"
當?pytest.fixture(scope="function") 時,pytest的yieId 類似unittest的teartown。每個方法(函數)都會執行一次
1.新建?test_function1.py文件,我們看一下是不是這樣的。
2.1 代碼實現:
2.2 參考代碼:
# coding=utf-8
# 1.導入模塊
import pytest@pytest.fixture(scope="function")
def login():print("登錄成功")yieldprint("用例執行完成,收尾")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.py"])
2.3 運行結果:
運行代碼后,控制臺打印如下圖的結果
從結果看出,雖然test1,test2,test3三個地方都調用了login函數,并且它會在每一個用例前執行一次
2.如果test1不調用,test2(調用login),test3不調用,運行順序會是怎樣的?
2.4 參考代碼:
# coding=utf-8
# 1.導入模塊
import pytest@pytest.fixture(scope="function")
def login():print("登錄成功")yieldprint("用例執行完成,收尾")def test1():print('操作1')print("-----------------------------------------------")def test2(login):print('操作2')print("-----------------------------------------------")def test3():print('操作3')print("-----------------------------------------------")if __name__ == "__main__":pytest.main(["-s", "test.py"])
2.5 運行結果:
運行代碼后,控制臺打印如下圖的結果
從結果看出,function級別的fixture在當前.py模塊里,只會在用例(test_s2)第一次調用前執行一次
3.scope="module"
1.fixture參數scope=”module”,module作用是整個.py文件都會生效( 整個文件只會執行一次),用例調用時,參數寫上函數名稱就行
3.1 代碼實現:
3.2 參考代碼:
# coding=utf-8
# 1.導入模塊
import pytest@pytest.fixture(scope="module")
def login():print("登錄成功")yieldprint("用例執行完成,收尾")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_bjhg_function1.py"])
3.3 運行結果:
運行代碼后,控制臺打印如下圖的結果
從結果看出,雖然test1,test2,test3三個地方都調用了login函數,但是它只會在第一個用例前執行一次
2.如果test1不調用,test2(調用login),test3不調用,運行順序會是怎樣的?
3.4 參考代碼:
# coding=utf-8
# 1.導入模塊
import pytest@pytest.fixture(scope="module")
def login():print("登錄成功")yieldprint("用例執行完成,收尾")def test1():print('操作1')print("-----------------------------------------------")def test2(login):print('操作2')print("-----------------------------------------------")def test3():print('操作3')print("-----------------------------------------------")if __name__ == "__main__":pytest.main(["-s", "test.py"])
3.5 運行結果:
運行代碼后,控制臺打印如下圖的結果
從結果看出,module級別的fixture在當前.py模塊里,只會在用例(test_s2)第一次調用前執行一次
4. yield執行teardown
細心的童鞋或者小伙伴可以看到,我前邊的代碼中有一個yield關鍵字,大家有點好奇是做什么的,這一小節就給你答疑解惑。其實就是用來喚醒teardown。
1.fixture里面的teardown用yield來喚醒teardown的執行
4.1 代碼實現:
4.2 參考代碼:
# coding=utf-8
# 1.導入模塊
import pytest@pytest.fixture(scope="module")
def login():print("登錄成功")yieldprint("執行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.py"])
4.3 運行結果:
運行代碼后,控制臺打印如下圖的結果
5. yield遇到異常
1.如果其中一個用例出現異常,不影響yield后面的teardown執行,運行結果互不影響,并且在用例全部執行完之后,會呼喚teardown的內容
5.1 代碼實現:
5.2 參考代碼:
# coding=utf-8
# 1.導入模塊
import pytest@pytest.fixture(scope="module")
def login():print("登錄成功")yieldprint("執行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.py"])
5.3 運行結果:
運行代碼后,控制臺打印如下圖的結果
2.如果在setup就異常了,那么是不會去執行yield后面的teardown內容了
3.yield也可以配合with語句使用,以下是官方文檔給的案例
# 官方文檔案例
# content of test_yield2.pyimport smtplib
import pytest@pytest.fixture(scope="module")
def smtp():with smtplib.SMTP("smtp.gmail.com") as smtp:yield smtp # provide the fixture value
6.addfinalizer終結函數
1.除了yield可以實現teardown,在request-context對象中注冊addfinalizer方法也可以實現終結函數。
# 官方案例# content of conftest.py
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 smtp_connection")smtp_connection.close()request.addfinalizer(fin)return smtp_connection # provide the fixture value
2.yield和addfinalizer方法都是在測試完成后呼叫相應的代碼。但是addfinalizer不同的是:
他可以注冊多個終結函數。
這些終結方法總是會被執行,無論在之前的setup code有沒有拋出錯誤。這個方法對于正確關閉所有的fixture創建的資源非常便利,即使其一在創建或獲取時失敗
參考文檔:pytest documentation