摘要:所有的函數都使用的命名規則,以便于查找并且同其他函數區分開來。用來每個,保證被正確的定義。里還有一個選項,用來表示這個函數是個函數。自動注冊插件除了常規的方法注冊插件,同時提供了方法,允許通過自動注冊插件。
前言
參考官方的這篇文章,我嘗試翻譯其中一些重點部分,并且拓展了相關的pluggy部分的知識。由于pytest是在pluggy基礎上構建的,強烈建議先閱讀一下pluggy的官方文檔,這樣理解起來更加容易一點。
正文conftest.py可以作為最簡單的本地plugin調用一些hook函數,以此來做些強化功能。
pytest整個框架通過調用如下定義良好的hooks來實現配置,收集,執行和報告這些過程:
內置plugins:從代碼內部的_pytest目錄加載;
外部插件(第三方插件):通過setuptools entry points機制發現的第三方插件模塊;
conftest.py形式的本地插件:測試目錄下的自動模塊發現機制;
原則上,每個hook都是一個 1:N 的python函數調用, 這里的 N 是對一個給定hook的所有注冊調用數。所有的hook函數都使用pytest_xxx的命名規則,以便于查找并且同其他函數區分開來。
decorratorpluggy里提供了兩個decorator helper類,分別是HookspecMarker和HookimplMarker,通過使用相同的project_name參數初始化得到對應的裝飾器,后續可以用這個裝飾器將函數標記為hookspec和hookimpl。
hookspechook specification (hookspec)用來validate每個hookimpl,保證hookimpl被正確的定義。
hookspec 通過 add_hookspecs()方法加載,一般在注冊hookimpl之前先加載;
hookimplhook implementation (hookimpl) 是一個被恰當標記過的回調函數。hookimpls 通過register()方法加載。
注:為了保證hookspecs在項目里可以不斷演化, hookspec里的參數對于hookimpls是可選的,即可以定義少于spec里定義數量的參數。
hookwrapperhookimpl 里還有一個hookwrapper選項,用來表示這個函數是個hookwrapper函數。hookwrapper函數可以在普通的非wrapper的hookimpls執行的前后執行一些其他代碼, 類似于@contextlib.contextmanager,hookwrapper必須在它的主體包含單一的yield,用來實現生成器函數,例如:
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
生成器發送一個 pluggy.callers._Result對象 , 這個對象可以在 yield表達式里指定并且通過 force_result()或者get_result() 方法重寫或者拿到最終結果。
注:hookwrapper不能返回結果 (跟所有的生成器函數一樣);
hookimpl的調用順序默認情況下,hook的調用順序遵循注冊時的順序LIFO(后進先出),hookimpl允許通過tryfirst, trylast*選項調整這一項順序。
舉個例子,對于如下的代碼:
# 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
執行順序如下:
Plugin3的pytest_collection_modifyitems先調用,直到yield點,因為這是一個hook warpper。
Plugin1的pytest_collection_modifyitems被調用,因為有 tryfirst=True參數。
Plugin2的pytest_collection_modifyitems被調用,因為有 trylast=True參數 (不過即使沒有這個參數也會排在tryfirst標記的plugin后面)。
Plugin3的pytest_collection_modifyitems調用yield后面的代碼. yield接收非Wrapper的result返回. Wrapper函數不應該修改這個result。
當然也可以同時將 tryfirst 和 trylast與 hookwrapper=True 混用,這種情況下它將影響hookwrapper之間的調用順序.
hook執行結果處理和firstresult選項默認情況下,調用一個hook會使底層的hookimpl函數在一個循環里按順序執行,并且將其非空的執行結果添加到一個list里面。例外的是,hookspec里有一個firstresult選項,如果指定這個選項為true,那么得到第一個返回非空的結果的hookimpl執行后就直接返回,后續的hookimpl將不在被執行,參考后面的例子。
注: hookwrapper還是正常的執行
hook的調用每一個pluggy.PluginManager 都有一個hook屬性, 可以通過調用這個屬性的call函數來調用hook,需要注意的是,調用時必須使用關鍵字參數語法來調用。
請看下面這個firstresult和hook調用例子:
from pluggy import PluginManager, HookimplMarker, HookspecMarker hookspec = HookspecMarker("myproject") hookimpl = HookimplMarker("myproject") class MySpec1(object): @hookspec def myhook(self, arg1, arg2): pass class MySpec2(object): # 這里將firstresult設置為True @hookspec(firstresult=True) def myhook(self, arg1, arg2): pass class Plugin1(object): @hookimpl def myhook(self, arg1, arg2): """Default implementation. """ return 1 class Plugin2(object): # hookimpl可以定義少于hookspec里定義數量的參數,這里只定義arg1 @hookimpl def myhook(self, arg1): """Default implementation. """ return 2 class Plugin3(object): # 同上,甚至可以不定義hookspec里的參數 @hookimpl def myhook(self): """Default implementation. """ return 3 pm1 = PluginManager("myproject") pm2 = PluginManager("myproject") pm1.add_hookspecs(MySpec1) pm2.add_hookspecs(MySpec2) pm1.register(Plugin1()) pm1.register(Plugin2()) pm1.register(Plugin3()) pm2.register(Plugin1()) pm2.register(Plugin2()) pm2.register(Plugin3()) # hook調用必須使用關鍵字參數的語法 print(pm1.hook.myhook(arg1=None, arg2=None)) print(pm2.hook.myhook(arg1=None, arg2=None))
得到結果如下:
[3, 2, 1] 3
可以看到,由于pm2里的hookspec里有firstresult參數,在得到3這個非空結果時就直接返回了。
自動注冊插件除了常規的register()方法注冊插件,pluggy同時提供了 load_setuptools_entrypoints()方法,允許通過 setuptools entry points 自動注冊插件。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/43436.html
摘要:會調用向打印一些環境信息,比如插件版本,版本,操作平臺這些等。在執行之后調用執行的過程是否執行取決于是否需要創建執行的過程如果有。所有測試執行完畢之后,返回之前的階段。結束以后,整個退出之前的階段。 pytest插件開發需要熟悉一些常用的hook函數,官方對于這些hook有一份簡略的文檔(目前除了小部分hook目前缺乏文檔外,見這個issue),但是各個hook的調用邏輯沒有一個直觀的...
摘要:本套代碼和邏輯是本人的勞動成果,如果有轉載需要標注,非常適合公司做項目的同學小白也可以學哦接口自動化項目目錄公共方法的封裝如果不用配置文件可以使用這個方法進行封裝但是有一定的缺陷可以不使用字典。這是在正常的命令行解析之前發生的。 ...
摘要:其中用到編程等,還需要花更多的精力去深入學習,當每項技能都能掌握到一定深度,才能稱為一個完整的知識體系。 都有哪些種類的配置文件 pytest.ini:pytes...
摘要:問題大部分問題是因為安裝了導致的比如此時需要先卸載然后再安裝包已經安裝過的不用重復安裝。版本問題類似于這種一般是因為版本太高導致建議卸載現有版本并安裝較低版本的。后續重裝低版本出現如下報錯重裝最新版本并重裝包 ...
摘要:在測試行業,如果利用作為腳本語言開發自動化測試用例,可用的框架有等主流可供選擇,個人感覺較之和應該算是現階段最靈活,功能最全面,擴展最豐富的框架了。不知道各位在做自動化的時候有沒有遇到過用例數過多,單機執行效率不高的困擾。 在測試行業,如果利用python作為腳本語言開發自動化測試用例,可用...
閱讀 1561·2021-11-24 09:39
閱讀 1042·2021-11-22 15:11
閱讀 2167·2021-11-19 11:35
閱讀 1627·2021-09-13 10:37
閱讀 2453·2021-09-03 10:47
閱讀 2134·2021-08-30 09:47
閱讀 1626·2021-08-20 09:39
閱讀 2901·2019-08-30 14:13