失敗したテストを再実行し、テスト実行間で状態を維持する方法

使用法

プラグインは、最後の pytest 呼び出しから失敗を再実行するための 2 つのコマンドラインオプションを提供します:

  • --lf, --last-failed - 失敗のみを再実行します。

  • --ff, --failed-first - 失敗を最初に実行し、その後に残りのテストを実行します。

クリーンアップのために (通常は不要) 、--cache-clear オプションを使用すると、テスト実行前にすべてのセッション間キャッシュ内容を削除できます。

他のプラグインは、pytest 呼び出し間で json エンコード可能 な値を設定/取得するために config.cache オブジェクトにアクセスする場合があります。

注釈

このプラグインはデフォルトで有効になっていますが、必要に応じて無効にすることができます: 名前でプラグインを無効化/登録解除する を参照してください (このプラグインの内部名は cacheprovider です) 。

失敗のみを再実行するか、失敗を最初に実行する

まず、50 のテスト呼び出しを作成し、そのうち 2 つだけが失敗するようにしましょう:

# content of test_50.py
import pytest


@pytest.mark.parametrize("i", range(50))
def test_num(i):
    if i in (17, 25):
        pytest.fail("bad luck")

これを初めて実行すると、2 つの失敗が表示されます:

$ pytest -q
.................F.......F........................                   [100%]
================================= FAILURES =================================
_______________________________ test_num[17] _______________________________

i = 17

    @pytest.mark.parametrize("i", range(50))
    def test_num(i):
        if i in (17, 25):
>           pytest.fail("bad luck")
E           Failed: bad luck

test_50.py:7: Failed
_______________________________ test_num[25] _______________________________

i = 25

    @pytest.mark.parametrize("i", range(50))
    def test_num(i):
        if i in (17, 25):
>           pytest.fail("bad luck")
E           Failed: bad luck

test_50.py:7: Failed
========================= short test summary info ==========================
FAILED test_50.py::test_num[17] - Failed: bad luck
FAILED test_50.py::test_num[25] - Failed: bad luck
2 failed, 48 passed in 0.12s

次に、--lf を使用して実行すると:

$ pytest --lf
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 2 items
run-last-failure: rerun previous 2 failures

test_50.py FF                                                        [100%]

================================= FAILURES =================================
_______________________________ test_num[17] _______________________________

i = 17

    @pytest.mark.parametrize("i", range(50))
    def test_num(i):
        if i in (17, 25):
>           pytest.fail("bad luck")
E           Failed: bad luck

test_50.py:7: Failed
_______________________________ test_num[25] _______________________________

i = 25

    @pytest.mark.parametrize("i", range(50))
    def test_num(i):
        if i in (17, 25):
>           pytest.fail("bad luck")
E           Failed: bad luck

test_50.py:7: Failed
========================= short test summary info ==========================
FAILED test_50.py::test_num[17] - Failed: bad luck
FAILED test_50.py::test_num[25] - Failed: bad luck
============================ 2 failed in 0.12s =============================

前回の実行から失敗した 2 つのテストのみを実行し、48 の合格したテストは実行されていません (「選択解除」) 。

次に、--ff オプションを使用して実行すると、すべてのテストが実行されますが、最初の以前の失敗が最初に実行されます (FF とドットの一連の表示からわかるように) 。

$ pytest --ff
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 50 items
run-last-failure: rerun previous 2 failures first

test_50.py FF................................................        [100%]

================================= FAILURES =================================
_______________________________ test_num[17] _______________________________

i = 17

    @pytest.mark.parametrize("i", range(50))
    def test_num(i):
        if i in (17, 25):
>           pytest.fail("bad luck")
E           Failed: bad luck

test_50.py:7: Failed
_______________________________ test_num[25] _______________________________

i = 25

    @pytest.mark.parametrize("i", range(50))
    def test_num(i):
        if i in (17, 25):
>           pytest.fail("bad luck")
E           Failed: bad luck

test_50.py:7: Failed
========================= short test summary info ==========================
FAILED test_50.py::test_num[17] - Failed: bad luck
FAILED test_50.py::test_num[25] - Failed: bad luck
======================= 2 failed, 48 passed in 0.12s =======================

新しい --nf, --new-first オプション: 新しいテストを最初に実行し、その後に残りのテストを実行します。 どちらの場合も、テストはファイルの変更時間でソートされ、最新のファイルが最初に表示されます。

前回の実行でテストが失敗しなかった場合の動作

--lfnf/--last-failed-no-failures オプションは --last-failed の動作を制御します。 以前に (既知の) 失敗がない場合や、キャッシュされた lastfailed データが見つからなかった場合にテストを実行するかどうかを決定します。

2 つのオプションがあります:

  • all: 既知のテスト失敗がない場合、すべてのテスト (完全なテストスイート) を実行します。 これがデフォルトです。

  • none: 既知のテスト失敗がない場合、このことを示すメッセージを出力し、正常に終了します。

例:

pytest --last-failed --last-failed-no-failures all    # runs the full test suite (default behavior)
pytest --last-failed --last-failed-no-failures none   # runs no tests and exits successfully

新しい config.cache オブジェクト

プラグインや conftest.py サポートコードは、pytest config オブジェクトを使用してキャッシュされた値を取得できます。 ここに、pytest 呼び出し間で以前に作成された状態を再利用する fixture を実装する基本的な例のプラグインがあります:

# content of test_caching.py
import pytest


def expensive_computation():
    print("running expensive computation...")


@pytest.fixture
def mydata(pytestconfig):
    val = pytestconfig.cache.get("example/value", None)
    if val is None:
        expensive_computation()
        val = 42
        pytestconfig.cache.set("example/value", val)
    return val


def test_function(mydata):
    assert mydata == 23

このコマンドを初めて実行すると、print 文が表示されます:

$ pytest -q
F                                                                    [100%]
================================= FAILURES =================================
______________________________ test_function _______________________________

mydata = 42

    def test_function(mydata):
>       assert mydata == 23
E       assert 42 == 23

test_caching.py:19: AssertionError
-------------------------- Captured stdout setup ---------------------------
running expensive computation...
========================= short test summary info ==========================
FAILED test_caching.py::test_function - assert 42 == 23
1 failed in 0.12s

2 回目に実行すると、値はキャッシュから取得され、何も表示されません:

$ pytest -q
F                                                                    [100%]
================================= FAILURES =================================
______________________________ test_function _______________________________

mydata = 42

    def test_function(mydata):
>       assert mydata == 23
E       assert 42 == 23

test_caching.py:19: AssertionError
========================= short test summary info ==========================
FAILED test_caching.py::test_function - assert 42 == 23
1 failed in 0.12s

詳細については config.cache fixture を参照してください。

キャッシュ内容の検査

--cache-show コマンドラインオプションを使用して、キャッシュの内容を常に確認できます:

$ pytest --cache-show
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
cachedir: /home/sweet/project/.pytest_cache
--------------------------- cache values for '*' ---------------------------
cache/lastfailed contains:
  {'test_caching.py::test_function': True}
cache/nodeids contains:
  ['test_caching.py::test_function']
cache/stepwise contains:
  []
example/value contains:
  42

========================== no tests ran in 0.12s ===========================

--cache-show は、フィルタリングのためのグロブパターンを指定するオプション引数を受け取ります:

$ pytest --cache-show example/*
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
cachedir: /home/sweet/project/.pytest_cache
----------------------- cache values for 'example/*' -----------------------
example/value contains:
  42

========================== no tests ran in 0.12s ===========================

キャッシュ内容のクリア

次のように --cache-clear オプションを追加して、pytest にすべてのキャッシュファイルと値をクリアするように指示できます:

pytest --cache-clear

これは、速度よりも分離と正確性が重要な継続的インテグレーションサーバーからの呼び出しに推奨されます。

ステップワイズ

--lf -x の代替として、特にテストスイートの大部分が失敗すると予想される場合、--sw, --stepwise を使用すると、1 つずつ修正できます。 テストスイートは最初の失敗まで実行され、その後停止します。 次の呼び出しでは、テストは最後の失敗したテストから続行され、次の失敗したテストまで実行されます。 --stepwise-skip オプションを使用して、1 つの失敗したテストを無視し、2 番目の失敗したテストでテスト実行を停止することができます。 これは、失敗したテストで行き詰まり、それを後回しにしたい場合に便利です。 --stepwise-skip を指定すると、--stepwise も暗黙的に有効になります。