pytest 的 fixture 功能允許我們創(chuàng)建可重用的測(cè)試輔助函數(shù),通過(guò)內(nèi)省測(cè)試請(qǐng)求對(duì)象,我們可以獲取測(cè)試函數(shù)、類或模塊的上下文信息。這種能力在編寫(xiě)靈活且動(dòng)態(tài)的測(cè)試輔助函數(shù)時(shí)尤為重要。
你可能會(huì)疑惑,什么是測(cè)試請(qǐng)求對(duì)象?它為什么重要?
測(cè)試請(qǐng)求對(duì)象(request
)是 pytest 提供的一個(gè)特殊對(duì)象,它包含了當(dāng)前測(cè)試的上下文信息,例如測(cè)試函數(shù)、模塊、配置等。通過(guò)這個(gè)對(duì)象,我們可以使 fixture 根據(jù)不同的測(cè)試場(chǎng)景動(dòng)態(tài)調(diào)整行為。
假設(shè)我們有一個(gè)測(cè)試模塊,其中定義了一個(gè) smtpserver
變量,我們希望 fixture 能夠自動(dòng)讀取并使用這個(gè)變量。下面是一個(gè)簡(jiǎn)單的實(shí)現(xiàn):
# content of conftest.py
import pytest
import smtplib
@pytest.fixture(scope="module")
def smtp_connection(request):
# 從模塊中獲取 smtpserver 屬性,如果沒(méi)有則使用默認(rèn)值
server = getattr(request.module, "smtpserver", "smtp.gmail.com")
smtp_connection = smtplib.SMTP(server, 587, timeout=5)
yield smtp_connection
# 測(cè)試完成后執(zhí)行清理操作
print(f"finalizing {smtp_connection} ({server})")
smtp_connection.close()
在 smtp_connection
fixture 中,我們通過(guò) request.module
獲取當(dāng)前測(cè)試模塊對(duì)象,并嘗試讀取其 smtpserver
屬性。如果找不到該屬性,則使用默認(rèn)的 "smtp.gmail.com"
作為服務(wù)器地址。
接下來(lái),我們創(chuàng)建一個(gè)測(cè)試模塊,其中定義了 smtpserver
變量:
# content of test_anothersmtp.py
smtpserver = "mail.python.org" # 被 smtp fixture 讀取
def test_showhelo(smtp_connection):
assert 0, smtp_connection.helo()
運(yùn)行測(cè)試時(shí),smtp_connection
fixture 會(huì)使用 test_anothersmtp.py
模塊中的 smtpserver
變量:
$ pytest -s -q test_anothersmtp.py
輸出示例:
test_anothersmtp.py F
------------------------- Captured stdout teardown -------------------------
finalizing <smtplib.SMTP object at 0xdeadbeef0003> (mail.python.org)
smtp_connection
fixture 通過(guò) request.module
內(nèi)省當(dāng)前測(cè)試模塊,獲取 smtpserver
屬性。smtpserver
,則使用默認(rèn)值 "smtp.gmail.com"
。smtpserver
替換為與當(dāng)前項(xiàng)目相關(guān)的配置項(xiàng)。例如,如果你在使用編程獅(W3Cschool)的 API 測(cè)試框架,可以將 smtpserver
替換為 api_base_url
,并動(dòng)態(tài)讀取測(cè)試模塊中的 API 地址。除了從模塊中讀取屬性,我們還可以根據(jù)測(cè)試函數(shù)的參數(shù)動(dòng)態(tài)調(diào)整 fixture 的行為。例如:
# content of conftest.py
import pytest
@pytest.fixture
def dynamic_fixture(request):
# 獲取測(cè)試函數(shù)的參數(shù)
param = request.param
print(f"動(dòng)態(tài) fixture 參數(shù):{param}")
return param
在測(cè)試函數(shù)中使用參數(shù)化裝飾器:
# content of test_dynamic.py
import pytest
@pytest.mark.parametrize("dynamic_fixture", ["值1", "值2"], indirect=True)
def test_dynamic(dynamic_fixture):
print(f"測(cè)試函數(shù)接收到的參數(shù):{dynamic_fixture}")
assert True
運(yùn)行測(cè)試:
$ pytest -s -q test_dynamic.py
輸出示例:
test_dynamic.py .
動(dòng)態(tài) fixture 參數(shù):值1
測(cè)試函數(shù)接收到的參數(shù):值1
.
動(dòng)態(tài) fixture 參數(shù):值2
測(cè)試函數(shù)接收到的參數(shù):值2
在實(shí)際測(cè)試代碼中,我們可以通過(guò)設(shè)置測(cè)試模塊的屬性來(lái)實(shí)現(xiàn)更靈活的配置。例如:
# content of test_programminglion.py
# 動(dòng)態(tài)配置項(xiàng)
api_base_url = "https://api.programminglion.com"
test_environment = "production"
def test_api_connection(dynamic_fixture):
print(f"API 基礎(chǔ) URL:{api_base_url}")
print(f"測(cè)試環(huán)境:{test_environment}")
assert True
對(duì)應(yīng)的 fixture:
# content of conftest.py
import pytest
@pytest.fixture
def dynamic_fixture(request):
# 獲取測(cè)試模塊的配置
api_url = getattr(request.module, "api_base_url", "默認(rèn) URL")
environment = getattr(request.module, "test_environment", "默認(rèn)環(huán)境")
print(f"從模塊讀取的 API URL:{api_url}")
print(f"從模塊讀取的測(cè)試環(huán)境:{environment}")
return {
"api_url": api_url,
"environment": environment
}
運(yùn)行測(cè)試:
$ pytest -s -q test_programminglion.py
輸出示例:
test_programminglion.py .
從模塊讀取的 API URL:https://api.programminglion.com
從模塊讀取的測(cè)試環(huán)境:production
API 基礎(chǔ) URL:https://api.programminglion.com
測(cè)試環(huán)境:production
A1:使用 fixture 內(nèi)省測(cè)試上下文可以讓我們編寫(xiě)更加靈活和可重用的測(cè)試輔助函數(shù)。通過(guò)獲取測(cè)試模塊、類或函數(shù)的上下文信息,我們可以動(dòng)態(tài)調(diào)整 fixture 的行為以適應(yīng)不同的測(cè)試場(chǎng)景,減少重復(fù)代碼,提高測(cè)試效率。
A2:可以通過(guò) request.param
獲取測(cè)試函數(shù)的參數(shù),但需要在測(cè)試函數(shù)中使用 @pytest.mark.parametrize
裝飾器,并設(shè)置 indirect=True
參數(shù),如下所示:
@pytest.mark.parametrize("fixture_name", ["參數(shù)1", "參數(shù)2"], indirect=True)
def test_function(fixture_name):
...
A3:scope
參數(shù)用于指定 fixture 的作用域,即 fixture 的創(chuàng)建和銷毀時(shí)機(jī)。常見(jiàn)的作用域包括:
function
:每個(gè)測(cè)試函數(shù)執(zhí)行前創(chuàng)建,執(zhí)行后銷毀(默認(rèn)值)。class
:每個(gè)測(cè)試類執(zhí)行前創(chuàng)建,執(zhí)行后銷毀。module
:每個(gè)測(cè)試模塊執(zhí)行前創(chuàng)建,執(zhí)行后銷毀。session
:整個(gè)測(cè)試會(huì)話開(kāi)始時(shí)創(chuàng)建,結(jié)束時(shí)銷毀。選擇合適的作用域可以優(yōu)化測(cè)試性能并避免不必要的資源重復(fù)創(chuàng)建。
pytest 的 fixture 功能通過(guò)內(nèi)省請(qǐng)求對(duì)象,提供了強(qiáng)大的測(cè)試上下文感知能力。這種能力使得我們可以編寫(xiě)靈活、動(dòng)態(tài)且可重用的測(cè)試輔助函數(shù),顯著提升測(cè)試代碼的質(zhì)量和效率。
對(duì)于初學(xué)者來(lái)說(shuō),建議從簡(jiǎn)單的 fixture 開(kāi)始,逐步嘗試使用 request
對(duì)象獲取不同的上下文信息,觀察其對(duì)測(cè)試行為的影響。同時(shí),關(guān)注 pytest 的官方文檔和社區(qū)資源,及時(shí)了解最新的特性和最佳實(shí)踐。
關(guān)注編程獅(W3Cschool)平臺(tái),獲取更多 pytest 測(cè)試框架的教程和案例,提升你的測(cè)試開(kāi)發(fā)技能!
更多建議: