ここでは 属性をもつテスト関数のマーク の仕組みを使ったサンプルを紹介します。
次のようにカスタムメタデータでテスト関数を “マーク” できます:
# test_server.py の内容
import pytest
@pytest.mark.webtest
def test_send_http():
pass # アプリの webtest テストを実行
def test_something_quick():
pass
バージョン 2.2 で追加.
webtest でマークされたテストのみを実行するように制限できます:
$ py.test -v -m webtest
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4 -- /home/hpk/venv/0/bin/python
collecting ... collected 2 items
test_server.py:3: test_send_http PASSED
=================== 1 tests deselected by "-m 'webtest'" ===================
================== 1 passed, 1 deselected in 0.00 seconds ==================
もしくは逆に webtest を除く全てのテストを実行します:
$ py.test -v -m "not webtest"
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4 -- /home/hpk/venv/0/bin/python
collecting ... collected 2 items
test_server.py:6: test_something_quick PASSED
================= 1 tests deselected by "-m 'not webtest'" =================
================== 1 passed, 1 deselected in 0.00 seconds ==================
バージョン 2.2 で追加.
テストスイートにマーカーを登録するのは登録です:
# pytest.ini の内容
[pytest]
markers =
webtest: mark a test as a webtest.
テストスイートに存在するマーカーが調べます。次の一覧では、先ほど定義した webtest マーカーがあります:
$ py.test --markers
@pytest.mark.webtest: mark a test as a webtest.
@pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform.
@pytest.mark.xfail(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied.
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
プラグインからマーカーを追加して処理するサンプルについては カスタムマーカーとコマンドラインオプションによるテストの実行制御 を参照してください。
ノート
次のように明示的にマーカーを登録することを推奨します:
Python 2.6 か、それ以上のバージョンでコーディングしているなら、クラスのテストメソッド全てにマーカーを適用するために pytest.mark をクラスデコレーターとして使えます:
# test_mark_classlevel.py の内容
import pytest
@pytest.mark.webtest
class TestClass:
def test_startup(self):
pass
def test_startup_and_more(self):
pass
これは2つのテスト関数に直接デコレーターを適用するのと同じです。
Pythn 2.4 との後方互換性を維持するには、次のように TestClass に pytestmark 属性も設定できます:
import pytest
class TestClass:
pytestmark = pytest.mark.webtest
もしくは、複数のマーカーを使う必要がある場合はリストも使えます:
import pytest
class TestClass:
pytestmark = [pytest.mark.webtest, pytest.mark.slowtest]
モジュールレベルのマーカーも設定できます:
import pytest
pytestmark = pytest.mark.webtest
この場合、そのモジュール内で定義されている全ての関数とメソッドに適用されます。
指定した引数に一致する名前のテストを実行するには -k コマンドラインオプションを使います:
$ py.test -k send_http # 前節で定義したサンプルを実行
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
collecting ... collected 4 items
test_server.py .
=================== 3 tests deselected by '-ksend_http' ====================
================== 1 passed, 3 deselected in 0.01 seconds ==================
また、そのキーワードに一致するものを除く全てのテストを実行することもできます:
$ py.test -k-send_http
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
collecting ... collected 4 items
test_mark_classlevel.py ..
test_server.py .
=================== 1 tests deselected by '-k-send_http' ===================
================== 3 passed, 1 deselected in 0.01 seconds ==================
もしくは、クラスのみを選択するには次のようにします:
$ py.test -kTestClass
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
collecting ... collected 4 items
test_mark_classlevel.py ..
=================== 2 tests deselected by '-kTestClass' ====================
================== 2 passed, 2 deselected in 0.01 seconds ==================
プラグインは、カスタムマーカーを提供して、そのマーカーに基づく特別な振る舞いを実装します。これは、コマンドラインオプションと、名前付きの環境の値に特化したテストを実行するためのパラメーター化されたテスト関数マーカーを追加する自己完結型のサンプルです:
# conftest.py の内容
import pytest
def pytest_addoption(parser):
parser.addoption("-E", dest="env", action="store", metavar="NAME",
help="only run tests matching the environment NAME.")
def pytest_configure(config):
# 追加のマーカーを登録
config.addinivalue_line("markers",
"env(name): mark test to run only on named environment")
def pytest_runtest_setup(item):
if not isinstance(item, item.Function):
return
if hasattr(item.obj, 'env'):
envmarker = getattr(item.obj, 'env')
envname = envmarker.args[0]
if envname != item.config.option.env:
pytest.skip("test requires env %r" % envname)
この local プラグインを使うテストファイルです:
# test_someenv.py の内容
import pytest
@pytest.mark.env("stage1")
def test_basic_db_operation():
pass
そのテストが必要とするものではない別の環境を指定して実行する例です:
$ py.test -E stage2
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
collecting ... collected 1 items
test_someenv.py s
======================== 1 skipped in 0.00 seconds =========================
今度は正しく必要とする環境を指定して実行します:
$ py.test -E stage1
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
collecting ... collected 1 items
test_someenv.py .
========================= 1 passed in 0.00 seconds =========================
--markers オプションは利用できるマーカーの一覧を表示します:
$ py.test --markers
@pytest.mark.env(name): mark test to run only on named environment
@pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform.
@pytest.mark.xfail(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied.
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
テストスイート内でマーカーをたくさん使うと、テスト関数に対して数回マーカーが適用される場合があります。プラグインコードから、そういった全ての設定を読み込めます。サンプルを紹介します:
# test_mark_three_times.py の内容
import pytest
pytestmark = pytest.mark.glob("module", x=1)
@pytest.mark.glob("class", x=2)
class TestClass:
@pytest.mark.glob("function", x=3)
def test_something(self):
pass
ここでは、同じテスト関数に対して3回適用される “glob” マーカーがあります。conftest ファイルから次のようにしてそれを調べます:
# conftest.py の内容
def pytest_runtest_setup(item):
g = getattr(item.obj, 'glob', None)
if g is not None:
for info in g:
print ("glob args=%s kwargs=%s" %(info.args, info.kwargs))
標準出力を取得せずにこのテストを実行して、何が表示されるかを見てみましょう:
$ py.test -q -s
collecting ... collected 2 items
..
2 passed in 0.01 seconds
glob args=('function',) kwargs={'x': 3}
glob args=('class',) kwargs={'x': 2}
glob args=('module',) kwargs={'x': 1}