目錄:
- 安裝及入門
- 使用和調(diào)用方法
- 原有TestSuite使用方法
- 斷言的編寫(xiě)和報(bào)告
- Pytest fixtures:清晰 模塊化 易擴(kuò)展
- 使用Marks標(biāo)記測(cè)試用例
- Monkeypatching/對(duì)模塊和環(huán)境進(jìn)行Mock
- 使用tmp目錄和文件
- 捕獲stdout及stderr輸出
- 捕獲警告信息
- 模塊及測(cè)試文件中集成doctest測(cè)試
- skip及xfail: 處理不能成功的測(cè)試用例
- Fixture方法及測(cè)試用例的參數(shù)化
- 緩存: 使用跨執(zhí)行狀態(tài)
- unittest.TestCase支持
- 運(yùn)行Nose用例
- 經(jīng)典xUnit風(fēng)格的setup/teardown
- 安裝和使用插件
- 插件編寫(xiě)
- 編寫(xiě)鉤子(hook)方法
- 運(yùn)行日志
- API參考
- 優(yōu)質(zhì)集成實(shí)踐
- 片狀測(cè)試
- Pytest導(dǎo)入機(jī)制及sys.path/PYTHONPATH
- 配置選項(xiàng)
- 示例及自定義技巧
- Bash自動(dòng)補(bǔ)全設(shè)置
編寫(xiě)鉤子(hook)方法
鉤子(hook)方法驗(yàn)證和執(zhí)行
pytest為任何給定的鉤子(hook)規(guī)范調(diào)用已注冊(cè)插件的鉤子(hook)方法。讓我們看一下鉤子(hook)的典型鉤子(hook)方法,pytest在收集完所有測(cè)試項(xiàng)目后調(diào)用。pytest_collection_modifyitems(session, config,items)
當(dāng)我們pytest_collection_modifyitems在插件中實(shí)現(xiàn)一個(gè)方法時(shí),pytest將在注冊(cè)期間驗(yàn)證你是否使用了與規(guī)范匹配的參數(shù)名稱,如果沒(méi)有則拯救。
讓我們看一下可能的實(shí)現(xiàn):
def pytest_collection_modifyitems(config, items):
# called after collection is completed
# you can modify the ``items`` list
...
這里,pytest將傳入config(pytest配置對(duì)象)和items(收集的測(cè)試項(xiàng)列表),但不會(huì)傳入session參數(shù),因?yàn)槲覀儧](méi)有在方法簽名中列出它。這種動(dòng)態(tài)的“修剪”參數(shù)允許pytest“未來(lái)兼容”:我們可以引入新的鉤子(hook)命名參數(shù)而不破壞現(xiàn)有鉤子(hook)實(shí)現(xiàn)的簽名。這是pytest插件的一般長(zhǎng)期兼容性的原因之一。
請(qǐng)注意,除了pytest_runtest_*不允許引發(fā)異常之外的鉤子(hook)方法。這樣做會(huì)打破pytest運(yùn)行。
firstresult:首先停止非無(wú)結(jié)果
大多數(shù)對(duì)pytest鉤子(hook)的調(diào)用都會(huì)產(chǎn)生一個(gè)結(jié)果列表,其中包含被調(diào)用鉤子(hook)方法的所有非None結(jié)果。
一些鉤子(hook)規(guī)范使用該firstresult=True選項(xiàng),以便鉤子(hook)調(diào)用僅執(zhí)行,直到N個(gè)注冊(cè)方法中的第一個(gè)返回非None結(jié)果,然后將其作為整個(gè)鉤子(hook)調(diào)用的結(jié)果。在這種情況下,不會(huì)調(diào)用其余的鉤子(hook)方法。
hookwrapper:在其他鉤子(hook)周圍執(zhí)行
版本2.7中的新功能。
pytest插件可以實(shí)現(xiàn)鉤子(hook)包裝器,它包裝其他鉤子(hook)實(shí)現(xiàn)的執(zhí)行。鉤子(hook)包裝器是一個(gè)生成器方法,它只產(chǎn)生一次。當(dāng)pytest調(diào)用鉤子(hook)時(shí),它首先執(zhí)行鉤子(hook)包裝器并傳遞與常規(guī)鉤子(hook)相同的參數(shù)。
在鉤子(hook)包裝器的屈服點(diǎn),pytest將執(zhí)行下一個(gè)鉤子(hook)實(shí)現(xiàn),并以Result封裝結(jié)果或異常信息的實(shí)例的形式將其結(jié)果返回到屈服點(diǎn)。因此,屈服點(diǎn)本身通常不會(huì)引發(fā)異常(除非存在錯(cuò)誤)。
以下是鉤子(hook)包裝器的示例定義:
import pytest
@pytest.hookimpl(hookwrapper=True)
def pytest_pyfunc_call(pyfuncitem):
do_something_before_next_hook_executes()
outcome = yield
# outcome.excinfo may be None or a (cls, val, tb) tuple
res = outcome.get_result() # will raise if outcome was exception
post_process_result(res)
outcome.force_result(new_res) # to override the return value to the plugin system
請(qǐng)注意,鉤子(hook)包裝器本身不返回結(jié)果,它們只是圍繞實(shí)際的鉤子(hook)實(shí)現(xiàn)執(zhí)行跟蹤或其他副作用。如果底層鉤子(hook)的結(jié)果是一個(gè)可變對(duì)象,它們可能會(huì)修改該結(jié)果,但最好避免它。
有關(guān)更多信息,請(qǐng)參閱插件文檔。
鉤子(hook)方法排序/調(diào)用示例
對(duì)于任何給定的鉤子(hook)規(guī)范,可能存在多個(gè)實(shí)現(xiàn),因此我們通常將hook執(zhí)行視為 1:N方法調(diào)用,其中N是已注冊(cè)方法的數(shù)量。有一些方法可以影響鉤子(hook)實(shí)現(xiàn)是在其他人之前還是之后,即在N-sized方法列表中的位置:
# Plugin 1
@pytest.hookimpl(tryfirst=True)
def pytest_collection_modifyitems(items):
# will execute as early as possible
...
# Plugin 2
@pytest.hookimpl(trylast=True)
def pytest_collection_modifyitems(items):
# will execute as late as possible
...
# Plugin 3
@pytest.hookimpl(hookwrapper=True)
def pytest_collection_modifyitems(items):
# will execute even before the tryfirst one above!
outcome = yield
# will execute after all non-hookwrappers executed
這是執(zhí)行的順序:
- Plugin3的pytest_collection_modifyitems被調(diào)用直到屈服點(diǎn),因?yàn)樗且粋€(gè)鉤子(hook)包裝器。
- 調(diào)用Plugin1的pytest_collection_modifyitems是因?yàn)樗鼧?biāo)有
tryfirst=True。 - 調(diào)用Plugin2的pytest_collection_modifyitems因?yàn)樗粯?biāo)記
trylast=True(但即使沒(méi)有這個(gè)標(biāo)記,它也會(huì)在Plugin1之后出現(xiàn))。 - 插件3的pytest_collection_modifyitems然后在屈服點(diǎn)之后執(zhí)行代碼。yield接收一個(gè)
Result實(shí)例,該實(shí)例封裝了調(diào)用非包裝器的結(jié)果。包裝不得修改結(jié)果。
這是可能的使用tryfirst,并trylast結(jié)合還 hookwrapper=True處于這種情況下,它會(huì)影響彼此之間hookwrappers的排序。
聲明新鉤子(hook)
插件和conftest.py文件可以聲明新鉤子(hook),然后可以由其他插件實(shí)現(xiàn),以便改變行為或與新插件交互:
<dt>pytest_addhooks(*pluginmanager *)[來(lái)源]
在插件注冊(cè)時(shí)調(diào)用,允許通過(guò)調(diào)用添加新的掛鉤 。pluginmanager.add_hookspecs(module_or_class, prefix)
參數(shù): | pluginmanager(_pytest.config.PytestPluginManager) - pytest插件管理器
注意:
這個(gè)鉤子(hook)與之不相容hookwrapper=True。
鉤子(hook)通常被聲明為do-nothing方法,它們只包含描述何時(shí)調(diào)用鉤子(hook)以及期望返回值的文檔。
有關(guān)示例,請(qǐng)參閱xdist中的newhooks.py。
可選擇使用第三方插件的鉤子(hook)
由于標(biāo)準(zhǔn)的驗(yàn)證機(jī)制,如上所述使用插件中的新鉤子(hook)可能有點(diǎn)棘手:如果你依賴未安裝的插件,驗(yàn)證將失敗并且錯(cuò)誤消息對(duì)你的用戶沒(méi)有多大意義。
一種方法是將鉤子(hook)實(shí)現(xiàn)推遲到新的插件,而不是直接在插件模塊中聲明鉤子(hook)方法,例如:
# contents of myplugin.py
class DeferPlugin(object):
"""Simple plugin to defer pytest-xdist hook functions."""
def pytest_testnodedown(self, node, error):
"""standard xdist hook function.
"""
def pytest_configure(config):
if config.pluginmanager.hasplugin("xdist"):
config.pluginmanager.register(DeferPlugin())```