pytest(6)-Fixture(韌體)
什麼是韌體
Fixture 翻譯成中文即是韌體的意思。它其實就是一些函數,會在執行測試方法/測試函數之前(或之後)載入運行它們,常見的如介面用例在請求介面前資料庫的初始連接,和請求之後關閉資料庫的操作。
我們之前在APP UI自動化系列中已經介紹過unittest
的相關測試韌體,如setup
、teardown
等。而pytest
中提供了功能更加豐富的 Fixture,用於實現setup
、teardown
功能。
定義方式
使用@pytest.fixture()
進行定義,簡單示例如下:
import pytest
@pytest.fixture()
def before():
print("連接資料庫")
調用方式
調用單個fixture函數
-
方式一,使用fixture函數名作為參數
import pytest @pytest.fixture() def before(): print("連接資料庫") # 調用before def test_01(before): print("執行test_01")
-
方式二,使用
@pytest.mark.usefixtures('fixture函數名')
裝飾器import pytest @pytest.fixture() def before(): print("連接資料庫") # 調用before @pytest.mark.usefixtures('before') def test_01(): print("執行test_01")
-
方式三,使用
autouse
參數自動執行fixture
函數import pytest # fixture函數定義的時候使用autouse參數,作用域範圍內的測試用例會自動調用該fixture函數 @pytest.fixture(autouse=True) def before(): print("連接資料庫") # 自動調用before def test_01(): print("執行test_01")
三種方式調用後的結果都如下:
我們可以看到,先執行了fixture
函數,再執行測試函數。
調用多個fixture函數
import pytest
@pytest.fixture()
def before():
print("連接資料庫")
@pytest.fixture()
def before_s():
print("初始化數據")
def test_01(before, before_s):
print("執行test_01")
調用多個fixture
函數時,由前至後依次執行,所以test_01()
調用時先執行before
,再執行before_s
。
對fixture函數重命名
定義fixture
函數時,可以利用name
參數進行重命名,方便用於調用,示例如下:
import pytest
@pytest.fixture(name='db')
def connect_order_db():
print("連接資料庫")
def test_01(db):
print("執行test_01")
使用fixture傳遞測試數據
在執行完fixture
函數後,有時需要將該fixture
中得到到某些數據傳遞給測試函數/測試方法,用於後續的執行。
fixture
中提供普通傳遞和參數化傳遞兩種數據傳遞方式。
普通傳遞
示例如下:
import pytest
@pytest.fixture()
def before():
print("連接資料庫")
return "連接成功!"
def test_01(before):
print("執行test_01")
assert before == "連接成功!"
注意,如果自定義的fixture
函數有返回值,需要使用上面說的方式一調用才能獲取fixture
函數的返回值並傳入測試函數中,方式二就無法獲取返回值。
參數化傳遞
對fixture
函數進行參數化時,需要使用參數params
,並且需要傳入參數request
,簡單示例如下:
import pytest
test_params = [1, 2, 0]
@pytest.fixture(params=test_params)
def before(request):
result = request.param
return result
def test_02(before):
print("執行test_02")
assert before
if __name__ == '__main__':
pytest.main()
執行結果:
可以看到,因為所調用的fixture函數進行了參數化,雖然只有一個測試函數但執行了3次。
conftest.py
上面我們舉的例子都是把fixture函數放在測試用例模組裡面,但如果很多測試模組需要引用同一個fixture函數怎麼辦,這是時候就需要把它放在命名為conftest
的模組里,這樣同級或以下目錄中的測試用例便能調用這些自定義的fixture函數。
例如,有如下目錄:
├─testcase
│ │
│ ├─test_module_01
│ │ test_case_1.py
│ │ test_case_2.py
│ │
│ ├─test_module_02
│ │ test_case_3.py
test_module_01
中的test_case_1.py
與test_case_2.py
都需要調用同一個fixture
函數,那麼我們只需要在test_module_01
中新建conftest.py
並編寫這個fixture
函數即可,示例如下:
├─testcase
│ │
│ ├─test_module_01
│ │ conftest.py
│ │ test_case_1.py
│ │ test_case_2.py
│ │
│ ├─test_module_02
│ │ test_case_3.py
conftest.py
:
import pytest
@pytest.fixture(autouse=True)
def before():
print("連接資料庫")
test_case_1.py
:
def test_01():
print("執行test_01")
test_case_2.py
:
def test_02():
print("執行test_02")
這樣,執行這兩個模組的測試用例時會自動先去調用conftest.py
中的before()
函數。
假設test_module_02
中的test_case_3.py
也需要調用這個before()
函數,那麼這個時候我們就需要在上一層即testcase
中新建conftest.py
並編寫這個before()
函數,才能在test_case_3.py
中調用,如下:
├─testcase
│ │ conftest.py
│ │
│ ├─test_module_01
│ │ conftest.py
│ │ test_case_1.py
│ │ test_case_2.py
│ │
│ ├─test_module_02
│ │ test_case_3.py
conftest.py
只作用於同級
或以下
目錄中的測試模組,且需要注意,當以下
層級中存在了另一個conftest.py
,那麼以下層級將由另一個conftest.py
文件接管。
作用域
pytest
的fixture
作用域分session
、module
、class
、function
四個級別。在定義fixture
函數的時候通過scope
參數指定作用範圍,默認為function
。
session
,每次會話執行一次module
,每個測試模組執行一次class
,每個測試類執行一次function
,每個測試方法執行一次
注意,對於單獨定義的測試函數,class、function都會起作用,可以從下列示例中看出來。
測試目錄結構如下:
├─apiAutoTest
│ │ run.py
│ │
│ ├─testcase
│ │ │ conftest.py
│ │ │
│ │ ├─test_module_02
│ │ │ │ conftest.py
│ │ │ │ test_case_3.py
│ │ │ │ test_case_4.py
其中conftest.py
程式碼如下:
import pytest
@pytest.fixture(scope="session", autouse=True)
def session_fixture():
print("這是一個作用於session的fixture")
@pytest.fixture(scope="module", autouse=True)
def module_fixture():
print("這是一個作用於module的fixture")
@pytest.fixture(scope="class", autouse=True)
def class_fixture():
print("這是一個作用於class的fixture")
@pytest.fixture(scope="function", autouse=True)
def function_fixture():
print("這是一個作用於function的fixture")
test_case_3.py
程式碼如下:
import pytest
class TestOrder:
def test_a(self):
print("test_a")
def test_b(self):
print("test_b")
def test_c():
print("test_c")
test_case_4.py
程式碼如下:
def test_e():
print("test_e")
run.py
程式碼如下:
import pytest
if __name__ == '__main__':
pytest.main(["-s"])
運行run.py
,結果如下:
collected 4 items
testcase\test_module_02\test_case_3.py
這是一個作用於session的fixture
這是一個作用於module的fixture
這是一個作用於class的fixture
這是一個作用於function的fixture
test_a
.這是一個作用於function的fixture
test_b
.這是一個作用於class的fixture
這是一個作用於function的fixture
test_c
.
testcase\test_module_02\test_case_4.py
這是一個作用於module的fixture
這是一個作用於class的fixture
這是一個作用於function的fixture
test_e
.
============================== 4 passed in 0.04s ==============================
從結果可以看出來:
- 作用於
session
的fixture函數只在所有測試用例執行之前調用了一次 - 作用於
module
的fixture函數在每個測試模組執行之前調用了一次 - 作用於
class
的fixture函數在每個測試類執行之前調用了一次 - 作用於
function
的fixture函數在每個測試方法/測試函數執行之前調用了一次
注意,在定義的測試函數(如test_c()
、test_e()
)執行之前也會調用scope=class的fixture函數。
總結
與unittest框架比較,pytest中
的Fixture
更加豐富,可擴展性更高。
Fixture
還有很多更加優雅的用法用於自動化測試項目中,本文只是以最簡單的示例進行說明。