Pytest系列(五)fixture
一、fixture 簡介
在 pytest 中加入fixture的目的是提供一個固定的基準,使測試能夠可靠、重複地執行,pytest 的 fixture 比傳統 xUnit 風格的setup/teardown 函式相比,有了巨大的改進:
-
fixture 具有明確的名稱,並通過在測試函式、模組、類或整個專案中宣告它們的使用來啟用。
-
fixture 是以模組化的方式實現的,因為每個fixture名稱都會觸發fixture函式,其本身也可以使用其他fixture。
-
fixture 管理從簡單的單元擴充套件到複雜的函式測試,允許根據配置和元件選項引數化fixture和測試,或者在函式、類、模組或整個測試會話範圍內重複使用fixture。
二、fixture 引數列表
@pytest.fixture(scope="function", params=None, autouse=False, ids=None, name=None)
def test():
print("fixture引數列表")
-
scope:可以理解成fixture的作用域,預設:function,還有class、module、package、session
-
autouse:預設:False,需要用例手動呼叫該fixture;如果是True,所有作用域內的測試用例都會自動呼叫該fixture
-
name:預設:裝飾器的名稱,同一模組的fixture相互呼叫建議寫不同的name
三、fixture 作用範圍
可以通過 scope 引數控制 fixture 的作用範圍:session>module>class>function
-
function:預設為function,每個函式或方法都呼叫一次
-
class:每個類呼叫一次,一個類中可以有多個方法
-
module:每個.py檔案呼叫一次
-
package:還處於測試階段...
-
session:每次執行呼叫一次
# test_fixture.py 檔案 import pytest import requests @pytest.fixture def httpbin_url(): print("fixture 函式呼叫了一次") return "http://httpbin.org" class TestHttp: def test_get(self, httpbin_url): res = requests.get(httpbin_url + "/get") assert res.status_code == 200 def test_post(self, httpbin_url): res = requests.post(httpbin_url + "/post") assert res.status_code == 200 def test_del(self, httpbin_url): res = requests.delete(httpbin_url + "/delete") assert res.status_code == 200 if __name__ == '__main__': pytest.main(['-s', 'test_fixture.py'])
四、呼叫 fixture 的三種方法
4.1 將 fixture 作為函式引數
Fixtures 允許測試用例引入預先定義好的 fixture 初始化準備函式(函式名不能以test開頭),函式可以通過 @pytest.fixture 裝飾器註冊成為一個 fixture 函式,測試用例可以使用 fixture 函式名來接收 fixture 物件。
import pytest
import requests
@pytest.fixture
def httpbin():
return ("get", "http://httpbin.org")
# 也可以以元祖的形式返回多個引數
def test_get(httpbin):
res = requests.request(httpbin[0], httpbin[1])
assert res.status_code == 200
4.2 使用裝飾器 @pytest.mark.usefixtures() 修飾需要執行的用例
如果一個方法或者一個class用例想要同時呼叫多個 fixture,可以使用 @pytest.mark.usefixture() 進行疊加。注意疊加順序,先執行的放底層,後執行的放上層。
注:usefixtures 與直接傳入 fixture 引數的區別在於,usefixture 無法獲取到返回值
@pytest.fixture
def httpbin():
print("開始測試-----")
@pytest.mark.usefixtures("httpbin")
def test_get():
res = requests.get("http://httpbin.org")
assert res.status_code == 200
4.3 fixture自動使用autouse=True
當用例很多的時候,每次都傳這個引數就會很麻煩。可以將 fxiture 引數 autouse 設定為True,開啟自動使用fixture功能,這樣用例就不用每次都去傳參了。
import pytest
import requests
@pytest.fixture(autouse=True)
def httpbin():
print("開始測試-----")
def test_get():
res = requests.get("http://httpbin.org")
assert res.status_code == 200
4.4 conftest.py
若要使用來自多個測試檔案的 fixture 函式,可以將其移至conftest.py檔案,它會自動被pytest發現,而無需匯入 fixture 函式。
一個工程下可以建多個 conftest.py 的檔案,一般在工程根目錄下設定的 conftest 檔案起到全域性作用。在不同子目錄下也可以放 conftest.py 的檔案,作用範圍只能在該目錄下生效。
注:conftest.py 檔名是固定的,且不能跨模組呼叫
# conftest.py 檔案
import pytest
@pytest.fixture(autouse=True)
def httpbin():
print("開始測試-----")
# test_demo.py 檔案
import requests
def test_get():
res = requests.get("http://httpbin.org")
assert res.status_code == 200
五、引數化 fixture
可以新增 params 對 Fixture 函式進行引數化,通過使用 request 物件來訪問每個引數;也可在引數化Fixture方法時使用 pytest.param(),其方式與@ pytest.mark.parametrize一樣。
@pytest.fixture(scope="class", params=["get", "post", "delete"])
def method(request):
return request.param
# fixture函式通過特殊的request物件訪問每個引數
class TestHttp:
def test_get(self, method):
assert 1
六、fixture 實現 teardown
pytest.fixture 採用 yield 實現 setup 和 teardown 操作,yield 前面的程式碼為 setup 程式碼,yield 後面的程式碼為 teardown 程式碼.
@pytest.fixture(scope="function")
def func(request):
driver = webdriver.Chrome()
yield driver
driver.quit()
class TestFuncA:
def test_01(self, func):
func.get("https://www.baidu.com")
assert 1
注:如果程式碼在 yield關鍵字之前發生異常,則不會呼叫yield 之後的程式碼。可以選擇利用請求上下文物件的 addfinalizer 方法來註冊完成函式。
import pytest
from selenium import webdriver
@pytest.fixture(scope="function")
def func(request):
driver = webdriver.Chrome()
def fin():
driver.quit()
request.addfinalizer(fin)
return driver
class TestFuncA:
def test_01(self, func):
func.get("https://www.baidu.com")
assert 1
addfinalizer 與 yield 的區別:
-
可以註冊多個完成函式
-
無論 fixture 設定程式碼是否引發異常,完成函式將始終被呼叫