前言
- pytest提供了更加靈活的前后置父腕,通過
@pytest.fixture(scope="")
來定義不同范圍的前后置
1. 如何聲明和調(diào)用fixture
- 聲明: 使用
@pytest.fixture
標識的函數(shù)即可作為fixture
使用 - 調(diào)用:
fixture
和測試函數(shù)都可以調(diào)用书幕,只要在函數(shù)的入?yún)⒅兄苯邮褂?code>fixture的函數(shù)名稱即可- 代碼示例中
outer
調(diào)用了order
和inner
蜗字,就是fixture
調(diào)用fixture
的例子 - 代碼示例中
test_order
調(diào)用了order
和outer
仑撞,就是測試函數(shù)調(diào)用fixture
的例子
- 代碼示例中
- 使用
@pytest.mark.usefixtures("fixture_name")
裝飾測試函數(shù)調(diào)用,需要注意的是該方法無法獲取fixture
的返回值
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture(autouse=True)
def outer(order, inner):
order.append("outer")
class TestOne:
@pytest.fixture
def inner(self, order):
order.append("one")
def test_order(self, order, outer):
assert order == ["one", "outer"]
- 熟悉代碼的同學可能已經(jīng)發(fā)現(xiàn)上述代碼不對辐董,
outer
定義的范圍內(nèi)inner
未定義雾棺,理論上調(diào)用不同才對柱告,但我們執(zhí)行代碼是正常。因為pytest的執(zhí)行邏輯是以用例為中心的婆瓜,順序應該是先加載用例腳本內(nèi)容快集,當加載到測試函數(shù)時,執(zhí)行test_order
廉白,發(fā)現(xiàn)它需要order函數(shù)个初,于是去調(diào)order, 調(diào)用結(jié)束后調(diào)用outer
, 而在test_order
調(diào)用outer
時猴蹂,inner
已經(jīng)被加載了院溺,所以outer
可以順利的調(diào)用到inner
- 看下執(zhí)行結(jié)果
(venv) C:\測試文件夾\project\python\pytest_demo\fixture>pytest -sv test_fixture.py
======================================================= test session starts ========================================================
platform win32 -- Python 3.6.8, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- c:\program files (x86)\python36-32\python.exe
cachedir: .pytest_cache
rootdir: C:\測試文件夾\project\python\pytest_demo, configfile: pytest.ini
collected 1 item
test_fixture.py::TestOne::test_order PASSED
======================================================== 1 passed in 0.01s =========================================================
2. 調(diào)用順序
-
fixture
的調(diào)用順序取決于3個因素- 范圍:首先執(zhí)行更高作用域的fixture,從大到小分別為:
["session", "package", "module", "class", "function"]
- 依賴:當一個夾具請求另一個夾具時磅轻,首先執(zhí)行另一個夾具珍逸。例如fixture_a請求fixture_b,fixture_b會先執(zhí)行瓢省,因為a依賴于b它弄息,沒有它就不能運行。即使a不需要b的結(jié)果勤婚,它仍然需要先請求b
- 自動使用:
@pytest.fixture(autouse=True)
時為自動使用摹量,同一作用域(scope)
內(nèi)的自動使用的fixture優(yōu)先使用。需要注意的是,如果fixture_a是自動使用且其依賴于不自動使用的fixture_b時缨称,fixture_b也會變?yōu)樽詣邮褂媚移渥鳛閒ixture_a的依賴會比fixture_a提前執(zhí)行。這也是符合調(diào)用依賴的描述的
- 范圍:首先執(zhí)行更高作用域的fixture,從大到小分別為:
2.1 范圍和依賴
- 先看范圍的執(zhí)行代碼和依賴的執(zhí)行順序
# content of test_scope_dep.py
import pytest
@pytest.fixture(scope="session")
def order():
return []
@pytest.fixture
def func(order):
order.append("function")
@pytest.fixture(scope="class")
def cls(order):
order.append("class")
@pytest.fixture(scope="module")
def mod(order):
order.append("module")
@pytest.fixture(scope="package")
def pack(order):
order.append("package")
@pytest.fixture(scope="session")
def sess(order):
order.append("session")
class TestClass:
def test_order(self, func, cls, mod, pack, sess, order):
assert order == ["session", "package", "module", "class", "function"]
- 執(zhí)行看結(jié)果,
order
和sess
作用域都是session
,但sess
其依賴于order
,故order先執(zhí)行 - 其余的不同作用域的則按照順序執(zhí)行
session > package > module > class > function
(venv) C:\測試文件夾\project\python\pytest_demo\fixture>pytest -sv test_scope_dep.py
======================================================= test session starts ========================================================
platform win32 -- Python 3.6.8, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- c:\program files (x86)\python36-32\python.exe
cachedir: .pytest_cache
rootdir: C:\測試文件夾\project\python\pytest_demo, configfile: pytest.ini
collected 1 item
test_scope_dep.py::TestClass::test_order PASSED
======================================================== 1 passed in 0.01s =========================================================
2.2. 自動使用
- 先看代碼
@pytest.fixture
def order():
return []
@pytest.fixture
def a(order):
order.append("a")
@pytest.fixture
def b(a, order):
order.append("b")
@pytest.fixture(autouse=True)
def c(order):
order.append("c")
def test_order(b, order):
assert order == ["c", "a", "b"]
執(zhí)行看結(jié)果, 所有fixture的作用域都是
function
,在測試時僅調(diào)用了
b
, 但因為c
是自動使用睦尽,故c先執(zhí)行c依賴于
order
, 所以是先調(diào)用了order
再在列表中添加'c'
然后調(diào)用
b
,b
依賴于a
,故先執(zhí)行a
,并在列表中添加元素'a'
最后執(zhí)行
b
,并在列表中添加'b'
執(zhí)行順序
order > c > a > b
-
注意事項
- 盡管
order
是其他fixture
的依賴項被調(diào)用過器净,但是測試函數(shù)中依然要主動調(diào)用,不然是無法獲取其返回值的当凡。如果order
是無返回值的fixture
山害,則無需調(diào)用
- 盡管
看到此處,大家可能發(fā)現(xiàn)
fixture
到目前為止也僅僅是描述了前置的信息沿量,那么如何處理teardown
呢
3. fixture
聲明后置 -- yield
關鍵字
# content of test_fixture_teardown.py
import pytest
@pytest.fixture
def yield_teardown():
print('\n################ setup part ##############')
yield True
print('\n############## teardown part ##############')
def test_yield(yield_teardown):
print('the case use yield_teardown result is {}'.format(yield_teardown))
- 在yield關鍵字前的代碼為前置浪慌,yield后的代碼為后置
- 執(zhí)行看下結(jié)果,確實按照預期打印
- 需要注意的是朴则,這種
fixture
寫法中如果不需要返回值時,yield
關鍵字后為空即可,但一定要有該關鍵字
(venv) C:\測試文件夾\project\python\pytest_demo\fixture>pytest -sv test_fixture_teardown.py
======================================================= test session starts ========================================================
platform win32 -- Python 3.6.8, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- c:\program files (x86)\python36-32\python.exe
cachedir: .pytest_cache
rootdir: C:\測試文件夾\project\python\pytest_demo, configfile: pytest.ini
collected 1 item
test_fixture_teardown.py::test_yield
################ setup part ##############
the case use yield_teardown result is True
PASSED
############## teardown part ##############
======================================================== 1 passed in 0.01s =========================================================
4. 參數(shù)化fixture
假設一種集群測試場景权纤,有兩臺主機上需要執(zhí)行同樣的用例,那么如果在每個用例上都去寫兩個參數(shù)是不是很麻煩乌妒⌒谙耄可不可以在fixture上完成參數(shù)化,讓同一條用例根據(jù)fixture上的參數(shù)化執(zhí)行兩次撤蚊。即通過fixture的參數(shù)化實現(xiàn)所有用例的多主機測試古掏。下面以多個測試郵箱連接為例:
在
fixture
函數(shù)裝飾器中增加params
參數(shù)用于實現(xiàn)參數(shù)化在
fixture
函數(shù)的形參列表中增加request
(固定參數(shù),不可修改)拴魄,用于接收params傳入的參數(shù)列表在
fixture
函數(shù)的內(nèi)部調(diào)用request.param
即可獲得列表中的元素
# content of test_fixture_para.py
import pytest
import smtplib
@pytest.fixture(scope="module", params=["smtp.163.com", "smtp.126.com"])
def smtp_connection(request):
smtp_connection = smtplib.SMTP(request.param, 25, timeout=5)
yield smtp_connection
print("\n ############### finalizing {}".format(smtp_connection))
smtp_connection.close()
def test_fixture_param(smtp_connection):
print('smtp_connection is {}'.format(smtp_connection))
- 執(zhí)行一下用例
(venv) C:\測試文件夾\project\python\pytest_demo\fixture>pytest -sv test_fixture_para.py
======================================================= test session starts ========================================================
platform win32 -- Python 3.6.8, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- c:\program files (x86)\python36-32\python.exe
cachedir: .pytest_cache
rootdir: C:\測試文件夾\project\python\pytest_demo, configfile: pytest.ini
collected 2 items
test_fixture_para.py::test_fixture_param[smtp.163.com] smtp_connection is <smtplib.SMTP object at 0x03778150>
PASSED
test_fixture_para.py::test_fixture_param[smtp.126.com]
############### finalizing <smtplib.SMTP object at 0x03778150>
smtp_connection is <smtplib.SMTP object at 0x037781F0>
PASSED
############### finalizing <smtplib.SMTP object at 0x037781F0>
======================================================== 2 passed in 0.69s =========================================================
- 假想一種接口測試的情況冗茸,通常web系統(tǒng)都需要的登錄后進行測試席镀,那么一般登錄就會放到前置中匹中,但是每個測試者或者每個測試場景中用戶名和密碼可能都不相同,那么能不能把用戶豪诲、密碼作為參數(shù)傳給前置函數(shù)顶捷,并返回認證所需數(shù)據(jù)呢,用戶名和密碼這種多個數(shù)據(jù)就需要使用
dict
去傳參屎篱,上代碼
@pytest.fixture(params=[{'user': 'user1', 'passwd': '1'}, {'user': 'user2', 'passwd': '2'}])
def login(request):
user = request.param['user']
passwd = request.param['passwd']
token = user + passwd
return token
def test_login(login):
print('\nthe token is {}'.format(login))
5. conftest.py
: 跨多個文件共享fixture
- 寫到這里有人就會問服赎,這種前置只是在當前文件生效,但登錄明顯是公共的前置交播,怎么讓其共享呢重虑,name就需要使用
conftest.py
文件定義fixture
- 該文件用作為整個目錄提供
fixture
的一種方式。在某個目錄的conftest.py
文件中定義的fixture
可以被該包中的任何測試用例直接使用而無需導入它們(pytest 將自動發(fā)現(xiàn)它們) - 每個目錄都可以有自己的
conftest.py
,其共享范圍為本目錄到其所有的遞歸子目錄