プログラミング
不安定なPythonテストの原因を見つけるためのツール
A tool for finding the causes of unstable Python tests (github.com)
要約
pytest-leak-finderは、単体ではパスするものの、テストスイート全体で実行すると失敗するような不安定なPythonテストの原因を特定するのに役立つpytestプラグインです。このツールは、git bisectに似たバイナリサーチのアプローチを用いて、失敗するテストの前に実行されたテスト群の中から問題のあるテストを絞り込みます。
全文翻訳
pytest-leak-finder
単体で実行するとパスするのに、スイート全体を実行すると失敗するテストがあります。何が起きているのでしょうか?前のテストが何かを汚いままにしている、というのが私の見解です。しかし、それはどのテストでしょうか?前のテストがたくさんあるかもしれませんね。
このプラグインは、ターゲットとなるテストの前に収集されたテストに対してバイナリサーチ(git bisect風)を行うことで、原因を特定するのに役立ちます。最初の実行では、失敗するテスト(ターゲット)に加えて、その前のテストの半分を収集します。ターゲットが失敗した場合、それは正しいパスを進んでいることになるので、新しいバイセクトが適用されます。ターゲットが失敗しない場合は、次にバイセクトする「半分」を変更します。
以下の例を考えてみましょう。
$ pytest -v demo/test_demo.py
collected 6 items
tests/test_demo.py::test1 PASSED
tests/test_demo.py::test2 PASSED
tests/test_demo.py::test3 PASSED
tests/test_demo.py::test4 PASSED
tests/test_demo.py::test5 FAILED
tests/test_demo.py::test6 PASSED
$ pytest -v --lf demo/test_demo.py
collected 6 items / 5 deselected / 1 selected
tests/test_demo.py::test5 PASSED
pytest-leak-finderを使用して問題のあるテストを見つけることができます。最初の実行では、失敗したテストが「ターゲット」として設定され、セッションが停止します。
$ pytest -v --leak-finder demo/test_demo.py
collected 6 items
tests/test_demo.py::test1 PASSED
tests/test_demo.py::test2 PASSED
tests/test_demo.py::test3 PASSED
tests/test_demo.py::test4 PASSED
tests/test_demo.py::test5 FAILED
===================================================== Leak finder =====================================================
Target set to: pytest-leak-finder/tests/test_demo.py::test5
Next step: a
Current target is: pytest-leak-finder/tests/test_demo.py::test5
============================================= 1 failed, 4 passed in 0.13s =============================================
2回目の実行では、ターゲットの前にパスしたテストの最初の半分(この例ではtest1とtest2で構成されるステップ「a」)を実行します。ターゲットがまだ失敗する場合、そのパスはさらに分割されて深く追跡されます。しかし、この例ではパスしたので、それを破棄し、他の半分にリークが含まれていたと仮定します。そのため、「次のステップ」は「ba」になります。
$ pytest -v --leak-finder demo/test_demo.py
collected 6 items / 3 deselected / 3 selected
demo/test_demo.py::test1 PASSED [ 33%]
demo/test_demo.py::test2 PASSED [ 66%]
demo/test_demo.py::test5 PASSED [100%]
===================================================== Leak finder =====================================================
We reach the target and nothing failed. Let's change the last half. Next step: ba
Current target is: pytest-leak-finder/demo/test_demo.py::test5
=========================================== 3 passed, 3 deselected in 0.03s ===========================================
したがって、新しいステップは「B-A」、つまりtest3になります。
$ pytest -v --leak-finder demo/test_demo.py
collected 6 items / 3 deselected / 3 selected
tests/test_demo.py::test3 PASSED
tests/test_demo.py::test5 FAILED
===================================================== Leak finder =====================================================
We found a leak! Leak found in: pytest-leak-finder/demo/test_demo.py::test3
Last step was: ba
And there it is, test3 was the problematic test we were looking for!
そして、探していた問題のあるテスト、test3が見つかりました!