pytestプラグインプローブ
20664 ワード
引用する
Pytestは強力なテストフレームワークであり、プラグイン化されたテストプラットフォームでもあります.デルは、使用中にサードパーティ製プラグインをインストールしてフレームワーク機能を強化することがよくあります.では、自分でもプラグインを書いてもいいですか?
公式サイトの定義によれば、1つ以上のhook関数を含むモジュールはpytestプラグインと理解できる.(A plugin contains one or multiple hook functions writing-plugins)
pytestのconftest.pyファイルにはhook関数が書けるので、conftet.pyはローカルプラグインであり、もう1つはpipでインストールされた外部サードパーティプラグインです.
ローカルプラグイン
次は簡単なhook方法をconftest.pyに書いて、すぐにローカルプラグインを実現しました.
外部プラグイン
次に、簡単な外部プラグインを書きます.ディレクトリ構造外部プラグインパッケージは、以下のように一般的なpythonパッケージディレクトリフォーマットに従います.ここでは主にpluginでmodule.pyにpytestプラグインのhook関数を入れ、setup.pyにパッケージ情報を記述します.外部プラグインパッケージを簡素化するために、ディレクトリの下の他のファイルは空のままです. コードプラグインの主な機能は、最も簡単なhello worldプログラム が示されている.パッケージアップロード パッケージpytestプラグイン アップロード テストプラグインダウンロードプラグイン
これで、サードパーティ製pytestプラグインの作成はすべて完了しました.キーはpytest hook付きmoduleを作成した後、setup.pyにpytest 11のentry_を提供することです.pointsでいいです.
##プラグインの読み込み順序は、conftest形式のローカルプラグインがある以上、外部サードパーティプラグインがある以上、プラグインの読み込み順序はどのようなものなのかを聞くかもしれません.公式文書で規定されている順序は以下のplugin-orderです. build-inプラグインをロードします.つまりpytest内蔵の_pytestフォルダのプラグイン 外部にインストールされているプラグインをロードします.setuptoolsが提供するentry_を検索します.pointsが見つけたモジュール -pパラメータで指定したプラグインをロードします.コマンドラインの-pパラメータを予めスキャンすることにより、指定されたplugin をロードする conftest.pyにロードされたプラグイン -traceconfigオプションを使用して、ロードされたすべてのプラグインを表示できます.
Pytestのhook管理コア:Pluggyフレームワーク
上記のプラグインは、pytestが定義したhookを呼び出すものです.どんなホックが使えますか?参考hook-reference
これらのhookはpytest_で冒頭にpytestのbootstrap,collection,test run,reportなどの各段階を網羅し,pytestフレームワークの各時期に柔軟に切り込み,カスタム機能を実現できる.
本質的にpytestフレームワークはhookの集合であり,異なる時期に異なるhookを実行し,各hookは1:Nのpython関数呼び出しである.
具体的にhook管理を担当するコードはPluggyフレームワークに統合され、Pytestフレームワークhook管理の核心はこのPluggyである.
このPluggyフレームワークの使い方を例に挙げてみましょう. Pluggyのコアは、3つのクラスHookspecMarker、HookimplMarker、PlugingManagerです.その主な論理はHookspecMarkerでhookをマークし、HookimplMarkerでhookの1つ以上の実装をマークすることである.最終的には、hookの登録(pm.add_hookspecs)、hook実装の登録(pm.register)、hookの実行(pm.hook.myhook)など、PluginManagerでhookを管理する. プロジェクト全体でグローバル一意のプロジェクトNameを使用する必要があります.この例では「myproject」 です. hook実行時、その複数のインプリメンテーションはregisterの順に進んでから出るので、ここではPlugin_を先に呼び出す2の実装、Plugin_の再呼び出し1にあります.順序を変更するには、@hookimplを呼び出すときにパラメータtryfirst(キューの末尾に挿入)またはtrylast(キューの先頭に挿入)を入力します. hookは、呼び出しが空の結果セット[]を返す実装がなくてもよい.利用可能pm.hook.myhook.get_hookimpls()はhookの実装オブジェクトを取得する.
Pluggyソースコード解読plugyソースコード解読参照
カスタムhook
pytestフレームワークでは、hook spec hookアクセサリーラベルは@hookspec,@hookimpl、PlugginManagerインスタンスオブジェクトはconfig.pluginmanagerです.こうしてみるとラベルもあるし、PluginManagerオブジェクトもあるので、私たちは完全に自分でhookを書くことができます.writinghooks hookspecはモジュールを書いて、@hookspecであなたのすべてのhookspecを宣言します. hookimplはpluginを書いてあなたのhookを含んで を実現します PluginManagerのadd_hookspecs,register用PluginManagerオブジェクトadd_hookspecs,registerはhook申明とhook実装を追加する.pytestは直接pytest_がありますaddhooksが利用できます. カスタムホックの使用準備が整いましたので、カスタムホックを使ってみてください.使用する鍵はpytestのconfigオブジェクトを見つけ、config.pluginmanager.hookで私たち自身のhookをcallすることです.(config.hookでcall、pytestでconfig.pluginmanager.hook=config.hook)configを取るには主に2つの方法があります.1つはpytestが持参したhookのパラメータからconfigオブジェクトを取ることです.2つはfixtureであれば、直接pytestconfigを導入してconfigオブジェクトを取ることができます. pytestのhookはpyest_で先頭、そうでないとpytestフレームワークは認識されません call hookの場合、キー値ペアでパラメータを渡す必要があります.「TypeError:hook calling supports only keyword arguments」
締めくくり
ローカルプラグインと外部プラグインを含むpytestプラグインの書き方を検討した.プラグインを書く核心はhookを書くことです.hookはpytestが持っているhookメソッドを書き換えることができ、pytestが適切なタイミングでhookを呼び出すか、hookをカスタマイズしてpytestフレームワークに登録し、自分でhookを呼び出すタイミングを手配することができます.
Pytestは強力なテストフレームワークであり、プラグイン化されたテストプラットフォームでもあります.デルは、使用中にサードパーティ製プラグインをインストールしてフレームワーク機能を強化することがよくあります.では、自分でもプラグインを書いてもいいですか?
公式サイトの定義によれば、1つ以上のhook関数を含むモジュールはpytestプラグインと理解できる.(A plugin contains one or multiple hook functions writing-plugins)
pytestのconftest.pyファイルにはhook関数が書けるので、conftet.pyはローカルプラグインであり、もう1つはpipでインストールされた外部サードパーティプラグインです.
ローカルプラグイン
次は簡単なhook方法をconftest.pyに書いて、すぐにローカルプラグインを実現しました.
# conftest.py
def pytest_runtest_call(item):
print('I am a simple pytest local plugin implementation')
外部プラグイン
次に、簡単な外部プラグインを書きます.
pytest_simple_plugin
├── setup.py
├── setup.cfg
├── README.rst
├── MANIFEST.in
└── pytest_simple_plugin
├── __init__.py
└── plugin_module.py
"""
File: plugin_module.py
Description: pytest plugin module file
"""
from pytest import fixture
@fixture(scope='module')
def hello(request):
name = request.config.getoption('--hello')
print(f'Hello {name if name else "world"}')
def pytest_addoption(parser):
parser.addoption('--hello', action='store',
default=None, help='say hi')
setup.pyキーを実現するentry_pointsにはプラグインがpytestプラグインとして登録され、プラグインのエントリモジュール"""
:Description: setup.py
"""
import setuptools
from os import path
with open(path.join(path.dirname(__file__), 'README.rst')) as f:
README = f.read()
setuptools.setup(
name='pytest-simple-plugin', # , pip
packages=['pytest_simple_plugin'],
version='0.0.4',
description='Simple pytest plugin',
long_description=README,
url='',
author='Li Liu',
author_email="[email protected]",
license='BSD License',
install_requires=['pytest'],
entry_points={ # pytest
'pytest11': ['pytest_simple_plugin = pytest_simple_plugin.plugin_module']
},
classifiers=[
"Framework :: Pytest",
],
keywords=['testing', 'pytest'],
python_requires='>=3',
)
python setup.py sdist
twine upload --repository-url https://upload.pypi.org/legacy/ -u -p dist/*
pip install pytest-simple-plugin
後、pytest関数呼び出しプラグインのhello fixtureを書きます.次の"""
File: test_hello.py
Description: test pytest plugin 'pytest-simple-plugin' hello fixure
"""
def test_hello(hello):
pass
でpytestを実行します.ここで、-sパラメータは-capture=noの略記です.失敗したテスト例の情報だけを出力するのではなく、すべてのprint出力を印刷することができます.$ pytest -s test_hello.py --hello Li
...
test_hello.py Hello Li
...
これで、サードパーティ製pytestプラグインの作成はすべて完了しました.キーはpytest hook付きmoduleを作成した後、setup.pyにpytest 11のentry_を提供することです.pointsでいいです.
##プラグインの読み込み順序は、conftest形式のローカルプラグインがある以上、外部サードパーティプラグインがある以上、プラグインの読み込み順序はどのようなものなのかを聞くかもしれません.公式文書で規定されている順序は以下のplugin-orderです.
Pytestのhook管理コア:Pluggyフレームワーク
上記のプラグインは、pytestが定義したhookを呼び出すものです.どんなホックが使えますか?参考hook-reference
これらのhookはpytest_で冒頭にpytestのbootstrap,collection,test run,reportなどの各段階を網羅し,pytestフレームワークの各時期に柔軟に切り込み,カスタム機能を実現できる.
本質的にpytestフレームワークはhookの集合であり,異なる時期に異なるhookを実行し,各hookは1:Nのpython関数呼び出しである.
具体的にhook管理を担当するコードはPluggyフレームワークに統合され、Pytestフレームワークhook管理の核心はこのPluggyである.
このPluggyフレームワークの使い方を例に挙げてみましょう.
import pluggy
hookspec = pluggy.HookspecMarker("myproject") #hook
hookimpl = pluggy.HookimplMarker("myproject") #hook
class MySpec(object):
"""hook """
@hookspec
def myhook(self, arg1, arg2):
pass
class Plugin_1(object):
"""hook 1"""
@hookimpl
def myhook(self, arg1, arg2):
#
print("inside Plugin_1.myhook()")
return arg1 + arg2
class Plugin_2(object):
"""hook 2"""
@hookimpl
def myhook(self, arg1, arg2):
#
print("inside Plugin_2.myhook()")
return arg1 - arg2
# PluginManager
pm = pluggy.PluginManager("myproject")
# hook
pm.add_hookspecs(MySpec)
# hook
pm.register(Plugin_1())
pm.register(Plugin_2())
# hook
results = pm.hook.myhook(arg1=1, arg2=2)
print(results)
# Output
# inside Plugin_2.myhook()
# inside Plugin_1.myhook()
# [-1, 3]
Pluggyソースコード解読plugyソースコード解読参照
カスタムhook
pytestフレームワークでは、hook spec hookアクセサリーラベルは@hookspec,@hookimpl、PlugginManagerインスタンスオブジェクトはconfig.pluginmanagerです.こうしてみるとラベルもあるし、PluginManagerオブジェクトもあるので、私たちは完全に自分でhookを書くことができます.writinghooks
"""
Filename: hooks.py
Description: hook module
"""
from pytest import hookspec
@hookspec
def pytest_my_hook(config):
"""my hook"""
"""
Filename: hooks_impl.py
Description: hook
"""
from pytest import hookimpl
class Plugin(object):
@hookimpl
def pytest_my_hook(config):
print('my first hook')
"""
Filename: conftest.py
"""
from . import hooks
from .my_plugin import myPlugin
def pytest_addhooks(pluginmanager):
pluginmanager.add_hookspecs(hooks)
pluginmanager.register(MyPlugin())
# pytest hook config
def pytest_runtest_call(item):
item.config.hook.pytest_my_hook(config=item.config)
# fixture pytestconfig pytestconfig
@pytest.fixture('session')
def my_fixture(pytestconfig):
print('my_fixture')
result = pytestconfig.hook.pytest_my_hook(config=pytestconfig)
注意すべき点は次の点です.締めくくり
ローカルプラグインと外部プラグインを含むpytestプラグインの書き方を検討した.プラグインを書く核心はhookを書くことです.hookはpytestが持っているhookメソッドを書き換えることができ、pytestが適切なタイミングでhookを呼び出すか、hookをカスタマイズしてpytestフレームワークに登録し、自分でhookを呼び出すタイミングを手配することができます.