Closed3

pytest の並列実行(pytest-xdist)

fujimotoshinjifujimotoshinji

pytest-xdist

pytest は機能拡張にオープンでプラグインを作って簡単に機能追加ができます。
pytest のプラグインは PyPI に 963 個あるらしい。

https://docs.pytest.org/en/7.0.x/reference/plugin_list.html

pytest-xdist も pytest のプラグインでインストールするとオプション指定するだけで並列テストを実行できます。

インストール

pip コマンドでインストールできます。

pip install pytest-xdist

利用方法

3秒待って文字列を返す関数を 3つテストするテストケースを作ります。

test_sleep.py
import time

def sleep_three_sec():
    time.sleep(3)
    return "three sec sleeped"

def test_sleep_three_sec_1():
    actual = sleep_three_sec()
    assert actual == "three sec sleeped"

def test_sleep_three_sec_2():
    actual = sleep_three_sec()
    assert actual == "three sec sleeped"

def test_sleep_three_sec_3():
    actual = sleep_three_sec()
    assert actual == "three sec sleeped"

通常通り指定すると 9秒かかります。

$ pytest test_sleep.py
======================================= test session starts =======================================
platform darwin -- Python 3.9.13, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /Users/fujimoto.shinji/Techs/python/python3-sandbox
plugins: xdist-2.5.0, forked-1.4.0
collected 3 items

test_sleep.py ...                                                                           [100%]

======================================== 3 passed in 9.05s ========================================

3並列で実行すると 4秒で終わります(マルチプロセス起動してテストを振り分けるオーバーヘッドがあるっぽい)

pytest -n 3 test_sleep.py
======================================= test session starts =======================================
platform darwin -- Python 3.9.13, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /Users/fujimoto.shinji/Techs/python/python3-sandbox
plugins: xdist-2.5.0, forked-1.4.0
gw0 [3] / gw1 [3] / gw2 [3]
...                                                                                         [100%]
======================================== 3 passed in 4.20s ========================================

非常に簡単です。テスト実装は一切変更する必要なく、pytest の実行オプションを指定するだけで並列テストを実現できました。

fujimotoshinjifujimotoshinji

オプション

pytest-xdist をインストールすることで以下がオプションに追加されます。

$ pytest --help

...

distributed and subprocess testing:
  -n numprocesses, --numprocesses=numprocesses
                        Shortcut for '--dist=load --tx=NUM*popen'. With 'auto', attempt to detect
                        physical CPU count. With 'logical', detect logical CPU count. If physical
                        CPU count cannot be found, falls back to logical count. This will be 0 when
                        used with --pdb.
  --maxprocesses=maxprocesses
                        limit the maximum number of workers to process the tests when using
                        --numprocesses=auto
  --max-worker-restart=MAXWORKERRESTART
                        maximum number of workers that can be restarted when crashed (set to zero
                        to disable this feature)
  --dist=distmode       set mode for distributing tests to exec environments.
                        each: send each test to all available environments.
                        load: load balance by sending any pending test to any available
                        environment.
                        loadscope: load balance by sending pending groups of tests in the same
                        scope to any available environment.
                        loadfile: load balance by sending test grouped by file to any available
                        environment.
                        loadgroup: like load, but sends tests marked with 'xdist_group' to the same
                        worker.
                        (default) no: run tests inprocess, don't distribute.
  --tx=xspec            add a test execution environment. some examples: --tx
                        popen//python=python2.5 --tx socket=192.168.1.102:8888 --tx
                        ssh=user@codespeak.net//chdir=testcache
  -d                    load-balance tests.  shortcut for '--dist=load'
  --rsyncdir=DIR        add directory for rsyncing to remote tx nodes.
  --rsyncignore=GLOB    add expression for ignores when rsyncing to remote tx nodes.
  --boxed               backward compatibility alias for pytest-forked --forked
  --testrunuid=TESTRUNUID
                        provide an identifier shared amongst all workers as the value of the
                        'testrun_uid' fixture,
                        ,if not provided, 'testrun_uid' is filled with a new unique string on every
                        test run.
  -f, --looponfail      run tests in subprocess, wait for modified files and re-run failing test
                        set until all pass.

-n numprocesses, --numprocesses=numprocesses

並列数(起動するプロセス数)の指定です。
テスト実行環境の CPU コア数、テストの負荷次第で指定します。
今回の私の利用方法ではテスト実行環境が 2 CPU コアですが、外部処理がメインなため、8並列で実行しています。

--dist=distmode

テストケースの分散方法の指定です。

指定値 挙動
each すべての環境にすべてのテストを送信します(使い道がわからない)
load テストケースレベルで分散します
loadscope テストモジュール、テストクラスレベルで分散します
同じクラスのテストケースは同じ環境で実行することを保証します
loadfile テストファイルレベルで分散します
同じファイルのテストケースは同じ環境で実行することを保証します
loadgroup 任意のテストケースをグループ化して分散します
グループ指定しないテストケースは load と同じ挙動です
このスクラップは2022/05/24にクローズされました