背景
コマンドラインインターフェイスの click のテスト方法についてのメモ
実装
コード
以下のコードをテストしたい場合を考える。
# src/main.py import click @click.group() def cli(): pass @cli.command() @click.option("--name", type=str, required=True, help="The person to greet.") def hello(name): if name == "Bob": raise ValueError("Sorry, Bob is not allowed.") click.echo(f"Hello {name}!") @cli.command() @click.option("--name", type=str, required=True, help="The person to greet.") def goodbye(name): click.echo(f"Goodbye {name}!")
# src/__main__.py from src.main import cli if __name__ == "__main__": cli()
通常の実行例は以下のようになる想定。
python -m src hello --name Alice
テスト方法
click.testing.CliRunner
click のテストでは click.testing.CliRunner
を使用する。
Testing Click Applications — Click Documentation (8.1.x)
上記の例では Bob
という引数が来たらエラーになるので、そのテストをしたいとする。
Pytest でのエラーケースのテストは pytest.raises
を使うので、同様のコードを書くと以下のようになる。
import pytest from click.testing import CliRunner def test_cli(name): from src.main import cli runner = CliRunner() with pytest.raises(ValueError): runner.invoke(cli, ["hello", "--name", "Bob"])
しかし、このテストを実行するとエラーが出る。
CliRunner.invoke
自体の実行はエラーが生じないためと思われる。
> with pytest.raises(ValueError): E Failed: DID NOT RAISE <class 'ValueError'> tests/test_main.py:9: Failed
click.testing.Result
click のテストでエラーが生じる場合のテストは CliRunner.invoke
の返り値を利用する
import pytest from click.testing import CliRunner def test_cli(): from src.main import cli runner = CliRunner() result = runner.invoke(cli, ["hello", "--name", "Bob"]) assert result.exit_code != 0 assert isinstance(result.exception, ValueError) assert str(result.exception) == "Sorry, Bob is not allowed."
以上を踏まえて、正常系と異常系のテストケースをまとめると以下のようになる。
import pytest from click.testing import CliRunner @pytest.mark.parametrize( "name, expected_error", [ ("Alice", None), ("Bob", ValueError("Sorry, Bob is not allowed.")), ], ) def test_cli(name, expected_error): from src.main import cli runner = CliRunner() result = runner.invoke(cli, ["hello", "--name", name]) if expected_error: assert result.exit_code != 0 assert isinstance(result.exception, ValueError) assert str(result.exception) == str(expected_error) else: assert result.exit_code == 0 assert result.output == f"Hello {name}!\n"
まとめ
- click のテストでは
click.testing.CliRunner
を使用する - 異常系のテストでは
CliRunner.invoke
の返り値のResult.exit_code
とResult.exception
を利用する