コマンドラインオプションで制御するテストを書きたいと仮定します。これを実現する基本的な方法は次の通りです:
# test_sample.py の内容
def test_answer(cmdopt):
if cmdopt == "type1":
print ("first")
elif cmdopt == "type2":
print ("second")
assert 0 # 何が表示されるかを見るため
このためにはコマンドラインオプションを追加する必要があります。 関数の引数 ファクトリーを使って cmdopt を提供します:
# conftest.py の内容
def pytest_addoption(parser):
parser.addoption("--cmdopt", action="store", default="type1",
help="my option: type1 or type2")
def pytest_funcarg__cmdopt(request):
return request.config.option.cmdopt
先ほど作成したコマンドラインオプションを指定せずに実行してみましょう:
$ py.test -q test_sample.py
collecting ... collected 1 items
F
================================= FAILURES =================================
_______________________________ test_answer ________________________________
cmdopt = 'type1'
def test_answer(cmdopt):
if cmdopt == "type1":
print ("first")
elif cmdopt == "type2":
print ("second")
> assert 0 # 何が表示されるかを見るため
E assert 0
test_sample.py:6: AssertionError
----------------------------- Captured stdout ------------------------------
first
1 failed in 0.01 seconds
次はコマンドラインオプションを指定して実行します:
$ py.test -q --cmdopt=type2
collecting ... collected 1 items
F
================================= FAILURES =================================
_______________________________ test_answer ________________________________
cmdopt = 'type2'
def test_answer(cmdopt):
if cmdopt == "type1":
print ("first")
elif cmdopt == "type2":
print ("second")
> assert 0 # 何が表示されるかを見るため
E assert 0
test_sample.py:6: AssertionError
----------------------------- Captured stdout ------------------------------
second
1 failed in 0.01 seconds
はい。基本的な使い方が分かりました。これ以外にも、テストの外部でコマンドラインオプションを処理して、別オブジェクトや複雑なオブジェクトを渡したいこともよくあります。次の例、もしくは現実の世界での例は Mysetup パターン: アプリケーションに特化したテストフィクスチャ を参照してください。
addopts を使って、プロジェクトにコマンドラインオプションを静的に追加できます。静的に追加したコマンドラインオプションが処理される前に、そのコマンドラインオプションを動的に変更することもできます:
# conftest.py の内容
import sys
def pytest_cmdline_preparse(args):
if 'xdist' in sys.modules: # pytest-xdist プラグイン
import multiprocessing
num = max(multiprocessing.cpu_count() / 2, 1)
args[:] = ["-n", str(num)] + args
xdist プラグイン をインストール済みなら、毎回 CPU 数に近いサブプロセスを使ってテストを実行できます。空のディレクトリで上記の conftest.py を実行します:
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
gw0 I / gw1 I / gw2 I / gw3 I
gw0 [0] / gw1 [0] / gw2 [0] / gw3 [0]
scheduling tests via LoadScheduling
============================= in 0.52 seconds =============================
slow とマークされたテストのスキップを制御するコマンドラインオプション --runslow を追加する conftest.py があります:
# conftest.py の内容
import pytest
def pytest_addoption(parser):
parser.addoption("--runslow", action="store_true",
help="run slow tests")
def pytest_runtest_setup(item):
if 'slow' in item.keywords and not item.config.getvalue("runslow"):
pytest.skip("need --runslow option to run")
テストモジュールは次のように書きます:
# test_module.py の内容
import pytest
slow = pytest.mark.slow
def test_func_fast():
pass
@slow
def test_func_slow():
pass
実行すると、”slow” テストがスキップされます:
$ py.test -rs # "-rs" は 's' の詳細をレポートします
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
collecting ... collected 2 items
test_module.py .s
========================= short test summary info ==========================
SKIP [1] /tmp/doc-exec-225/conftest.py:9: need --runslow option to run
=================== 1 passed, 1 skipped in 0.01 seconds ====================
もしくは slow とマークされたテストを実行します:
$ py.test --runslow
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
collecting ... collected 2 items
test_module.py ..
========================= 2 passed in 0.01 seconds =========================
テストから呼ばれるテストヘルパー関数があるなら、特定メッセージ付きでテストを失敗させる pytest.fail マーカーを使えます。 __tracebackhide__ オプションをヘルパー関数内にセットすると、そのテストヘルパー関数はトレースバックを表示しなくなります。サンプルを紹介します:
# test_checkconfig.py の内容
import pytest
def checkconfig(x):
__tracebackhide__ = True
if not hasattr(x, "config"):
pytest.fail("not configured: %s" %(x,))
def test_something():
checkconfig(42)
__tracebackhide__ 設定は、py.test のトレースバック表示に影響を与えます。 checkconfig 関数は、 --fulltrace コマンドラインオプションを指定しない限り、トレースバックを表示しません。この小さな関数を実行してみましょう:
$ py.test -q test_checkconfig.py
collecting ... collected 1 items
F
================================= FAILURES =================================
______________________________ test_something ______________________________
def test_something():
> checkconfig(42)
E Failed: not configured: 42
test_checkconfig.py:8: Failed
1 failed in 0.01 seconds
通常は、テストから呼ばれる場合にアプリケーションコードの振る舞いを分けるのは悪い考えです。しかし、アプリケーションコードがテストから実行されている場合に、確実に解明しなければならないことがあるなら、次のようなことができます:
# conftest.py の内容
def pytest_configure(config):
import sys
sys._called_from_test = True
def pytest_unconfigure(config):
del sys._called_from_test
アプリケーション内で sys._called_from_test というフラグをチェックします:
if hasattr(sys, '_called_from_test'):
# テスト内から実行時に呼ばれる
else:
# "普通" のときに呼ばれる
フラグを処理するために sys よりも独自のアプリケーションモジュールを使うのも良い考えです。
py.test の実行時に追加の情報を表示するのは簡単です:
# conftest.py の内容
def pytest_report_header(config):
return "project deps: mylib-1.1"
この関数はテストヘッダーに文字列を追加します:
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
project deps: mylib-1.1
collecting ... collected 0 items
============================= in 0.00 seconds =============================
複数行に渡る情報を扱うなら文字列のリストも返せます。当然レポートの情報量も制御できます。例えば、必要なときに情報を表示するために config.option.verbose の値で切り分けます:
# conftest.py の内容
def pytest_report_header(config):
if config.option.verbose > 0:
return ["info1: did you know that ...", "did you?"]
“–v” を指定して実行したときのみ追加の情報が表示されます:
$ py.test -v
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4 -- /home/hpk/venv/0/bin/python
info1: did you know that ...
did you?
collecting ... collected 0 items
============================= in 0.00 seconds =============================
何も指定せずに実行すると何も表示しません:
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
collecting ... collected 0 items
============================= in 0.00 seconds =============================
巨大なテストスイートの実行に時間がかかる場合、どのテストが最も遅いかを調べたいときがあります。擬似テストスイートで試してみましょう:
# test_some_are_slow.py の内容
import time
def test_funcfast():
pass
def test_funcslow1():
time.sleep(0.1)
def test_funcslow2():
time.sleep(0.2)
次にようにして、どのテスト関数が最も遅いかをプロファイルできます:
$ py.test --durations=3
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
collecting ... collected 3 items
test_some_are_slow.py ...
========================= slowest 3 test durations =========================
0.20s call test_some_are_slow.py::test_funcslow2
0.10s call test_some_are_slow.py::test_funcslow1
0.00s setup test_some_are_slow.py::test_funcslow2
========================= 3 passed in 0.31 seconds =========================