Python + pytestにて、コマンドラインオプションを追加して、コマンドラインから値を受け取った時のメモを残します。
目次
環境
- Python 3.6.3
- pytest 3.2.3
pytestのコマンドラインオプションの追加方法
公式ドキュメントに情報がありました。
Basic patterns and examples — pytest documentation
とすれば良いようです。
conftest.pyの実装
公式ドキュメントによると、コマンドラインオプションを実装するための引数は標準モジュールの argparse
と似ていました。
- 16.4. argparse — コマンドラインオプション、引数、サブコマンドのパーサー — Python 3.6.3 ドキュメント
- Argparse チュートリアル — Python 3.6.3 ドキュメント
そこで今回は、
の2つを実装してみます。
まずは、pytest_addoption()
関数に、追加するオプションの内容を記述します。
action='store'なものが前者、action='store_true'なものが後者となります。
def pytest_addoption(parser): """add commandline options""" parser.addoption('--fruit', action='store', default='ham', help='fruit name: apple, grape, banana, etc') parser.addoption('--season', action='store_true', help='fruit season now')
続いて、コマンドラインオプションから渡された値を返す、@pytest.fixtureデコレータの付いた関数を用意します。
request.config.getoption()
メソッドの引数に、コマンドラインオプション名(parser.addoption()の第一引数と同じ名前)を指定します。
@pytest.fixture def favorite_fruit(request): return request.config.getoption('--fruit') @pytest.fixture def is_season(request): return request.config.getoption('--season')
テストコードの実装
テストコードの引数に、conftext.pyで指定したfixtureデコレータのある関数の名前を指定します。
favorite_fruit
とis_season
が該当します。
def test_fruit(favorite_fruit, is_season): print(f'\nfruit:{favorite_fruit}, is_season:{is_season}') assert True
テストの実行
ヘルプを見ると、オプションが追加されていました。
$ python -m pytest e.g._add_commandline_option/ --help ... custom options: --fruit=FRUIT fruit name: apple, grape, banana, etc --season fruit season now ...
実行してみます。
なお、テストをパスした時はprint()
の結果が出力されないため、 -s
オプションを付けて実行します。
# 独自オプション無し:fruit・is_seasonともデフォルト値 $ python -m pytest e.g._add_commandline_option/ -s ... fruit:ham, is_season:False # fruitオプションあり:渡したbananaが表示 $ python -m pytest e.g._add_commandline_option/ -s --fruit banana ... fruit:banana, is_season:False # seasonオプションあり:is_seasonがTrueになった $ python -m pytest e.g._add_commandline_option/ -s --fruit banana --season ... fruit:banana, is_season:True
テストコードに、コマンドラインオプションの値が引き渡されていました。
応用:コマンドラインオプションで指定したテストコード以外はスキップする
pytestでは
pytest.mark.skip
でテストをスキップpytest.mark.skipif
で、条件に一致した場合にテストをスキップ
などの制御ができます。
ただ、上記の方法では、「コマンドラインオプションで指定したテストコード以外はスキップする」を実現できませんでした。コマンドラインオプションの値をマーカーに渡すことができなかったためです。
そこで公式ドキュメントを見たところ、
- 独自コマンドラインオプション
- 独自マーカー
を組み合わせれば良さそうでした。
Working with custom markers — pytest documentation
独自コマンドラインオプションを追加したときと同様、conftest.pyとテストコードに実装します。
conftest.pyの実装
conftest.pyには
pytest_addoption()
関数にて、独自コマンドラインオプションを追加 (今回は、targetオプション)pytest_configure()
関数にて、独自マーカーを追加 (今回は、pytest.mark.test_number)@pytest.fixture
デコレータの付いた関数にて、独自コマンドラインオプションの値を取得pytest_runtest_setup()
関数にて、スキップするかどうかを判定
の4つを実装しました。
def pytest_addoption(parser): """add commandline options""" parser.addoption('--target', action='store', help='if commandline args match test decorator, run test. if not, skip it') def pytest_configure(config): """add custom marker""" config.addinivalue_line('markers', 'test_number(number): test case number') @pytest.fixture def my_target(request): return request.config.getoption('--target') def pytest_runtest_setup(item): """decide skip or run testcase""" marker = item.get_marker('test_number') if marker is None: return opt = item.config.getoption('target') if opt is None: return targets = opt.split(',') test_number = str(marker.args[0]) if test_number not in targets: pytest.skip('it is non-target testcase. skip it.')
テストコードの実装
今回はテストコードとして、
- 独自マーカーがないテストコード
- 独自マーカーがあるテストコード
- parametrizeなデータごとに、独自マーカーを付けるテストコード
の3パターンを用意しました。
def test_no_marker(): print('apple') assert True @pytest.mark.test_number(1) def test_with_marker(): print('grape') assert True @pytest.mark.parametrize('kind, code', [ pytest.param('banana', '123', marks=pytest.mark.test_number(2)), pytest.param('pear', '456', marks=pytest.mark.test_number(3)), ]) def test_parameterize_with_marker(kind, code): print(f'\n{kind}: {code}') assert True
テストの実行
独自コマンドラインオプションのtargetを指定しない場合、すべてのテストが実行されました。
$ python -m pytest e.g._add_commandline_option/ -s ... .apple .grape . banana: 123 . pear: 456 .
target=1とした場合は、独自マーカーの引数に1を渡しているテストケースが実行されました。
また、独自マーカーがないテストも実行されています。
$ python -m pytest e.g._add_commandline_option/ -s --target 1 ... .apple .grape .ss
@pytest.mark.parametrizeなテストコードもうまく制御されています。
# parametrizeなテストのうち、前者のもの $ python -m pytest e.g._add_commandline_option/ -s --target 2 ... .apple .s banana: 123 .s # parametrizeなテストのうち、後者のもの $ python -m pytest e.g._add_commandline_option/ -s --target 3 ... .apple .ss pear: 456 .
conftest.pyについて
そもそも conftest.py
は何だろうと思い調べたところ、以下の解説が詳しかったです。ディレクトリレベルのプラグインと考えれば良さそうです。
python - In py.test, what is the use of conftest.py files? - Stack Overflow
ソースコード
GitHubにあげました。e.g._add_commandline_option
ディレクトリが今回のものです。
thinkAmi-sandbox/python_pytest-sample