什麼是韌體
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
還有很多更加優雅的用法用於自動化測試專案中,本文只是以最簡單的示例進行說明。