テスト関数が、特定のプラットフォームで実行できない、またはマークしたものが失敗することを期待する、またはテスト関数や setup 関数の実行中にヘルパー関数を呼び出すといった場合があります。
skip は、特定の設定や条件 (誤った Python インタープリターや依存関係の欠落など) がテスト関数を実行させない場合を除けば、テストが成功するのを期待します。 xfail は、テストそのものは実行しますが、実装上の問題からそのテストが失敗することを期待します。
py.test は skip と xfail テストを別々に数えて一覧表示します。しかし、スキップした/失敗したテストテストについての詳細な情報は、出力内容がごちゃごちゃにならないようにデフォルトでは表示されません。テストの進捗状況を表示する “短い” 文字に対応する詳細を見るために -r オプションが使えます:
py.test -rxs # skips と xfails の補足情報を表示
(コマンドラインオプションのデフォルト値の変更方法 を参照)
Python 3 インタープリターで実行するときにスキップするテスト関数をマークする例を紹介します:
import sys
@pytest.mark.skipif("sys.version_info >= (3,0)")
def test_function():
...
テスト関数の setup 処理中に skipif の条件が eval('sys.version_info >= (3,0)', namespace) を呼び出すことにより評価されます (バージョン 2.0.2 で追加) 。その名前空間は、例えば使っているモジュールのバージョンをチェックできるように、テスト関数の全モジュールの globals を含みます:
import mymodule
@pytest.mark.skipif("mymodule.__version__ < '1.2'")
def test_function():
...
このテスト関数は、 mymodule が指定したバージョンより低いときには実行されません (“スキップされる”) 。こういった条件を文字列で指定する主な理由は、py.test が skip 条件の概要をレポートできるからです。 namespace の構築に関する詳細は skipif/xfail 式の評価 を参照してください。
次のようにモジュールレベルで条件付きの skipif デコレーターのショートカットも作成できます:
win32only = pytest.mark.skipif("sys.platform != 'win32'")
@win32only
def test_function():
...
全ての関数を マークする のと同様に クラス全体またはモジュールレベル でテスト関数をスキップできます。プラットフォームによりテストクラスの全メソッドをスキップするサンプルを紹介します:
class TestPosixCalls:
pytestmark = pytest.mark.skipif("sys.platform == 'win32'")
def test_function(self):
"will not be setup or run under 'win32' platform"
pytestmark という特別な名前を使って、クラス内の各テスト関数へセットした関数を pytest に適用させます。テストコードが Python 2.6 以上を想定しているなら、もっと自然に skipif デコレーター (その他の任意のマーカー) をクラスに対して適用できます:
@pytest.mark.skipif("sys.platform == 'win32'")
class TestPosixCalls:
def test_function(self):
"will not be setup or run under 'win32' platform"
1つの関数に複数の “skipif” デコレーターを使うことは一般的に良いことです。いずれかの条件が適用された場合にそのテスト関数の実行はスキップされることになります。
テストの失敗を期待していることを表すのに xfail マーカーを使います:
@pytest.mark.xfail
def test_function():
...
このテストは実行されますが、失敗するときにトレースバックを表示しません。その代わり、ターミナル上に “expected to fail” か “unexpectedly passing” セクションにその一覧が表示されます。
コマンドラインで次のように指定すると:
pytest --runxfail
xfail でマークされていないかのように xfail でマークされたテスト関数を実行してレポートの表示を強制できます。
skipif と同様に、特定のプラットフォームでの失敗を期待するようにもマークできます:
@pytest.mark.xfail("sys.version_info >= (3,0)")
def test_function():
...
さらに、バグ ID といった reason を指定して “xfail” テストを実行しないようにもできます。他にも使用例と簡単なテストを紹介します:
import pytest
xfail = pytest.mark.xfail
@xfail
def test_hello():
assert 0
@xfail(run=False)
def test_hello2():
assert 0
@xfail("hasattr(os, 'sep')")
def test_hello3():
assert 0
@xfail(reason="bug 110")
def test_hello4():
assert 0
@xfail('pytest.__version__[0] != "17"')
def test_hello5():
assert 0
def test_hello6():
pytest.xfail("reason")
次のように xfail のレポートを表示するオプションを指定して実行します:
example $ py.test -rx xfail_demo.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
collecting ... collected 6 items
xfail_demo.py xxxxxx
========================= short test summary info ==========================
XFAIL xfail_demo.py::test_hello
XFAIL xfail_demo.py::test_hello2
reason: [NOTRUN]
XFAIL xfail_demo.py::test_hello3
condition: hasattr(os, 'sep')
XFAIL xfail_demo.py::test_hello4
bug 110
XFAIL xfail_demo.py::test_hello5
condition: pytest.__version__[0] != "17"
XFAIL xfail_demo.py::test_hello6
reason: reason
======================== 6 xfailed in 0.03 seconds =========================
バージョン 2.0.2 で追加.
pytest.mark.skipif(conditionstring) または pytest.mark.xfail(conditionstring) の条件文字列の評価は、次のように構築された名前空間ディクショナリの中で行われます:
pytest の config オブジェクトを使って、追加したテスト設定値によりスキップさせます:
@pytest.mark.skipif("not config.getvalue('db')")
def test_function(...):
...
インポート時に xfail の条件を宣言できない場合、テスト関数または setup コード内から xfail するように命令的に記述できます。サンプルを紹介します:
def test_function():
if not valid_config():
pytest.xfail("unsupported configuration")
モジュールレベル、またはテスト関数や setup 関数内から次のインポートヘルパーが使えます:
docutils = pytest.importorskip("docutils")
もし docutils がこの場所でインポートできないなら、このテストはスキップされます。ライブラリのバージョンによりスキップさせることもできます:
docutils = pytest.importorskip("docutils", minversion="0.3")
このバージョンは指定したモジュールの __ version__ 属性から読み込まれます。
何らかの理由でスキップ条件を宣言できない場合も、テスト関数または setup コード内からスキップするように命令的に記述できます。サンプルを紹介します:
def test_function():
if not valid_config():
pytest.skip("unsupported configuration")