测试 Python Click 命令异常

cry*_*bhu 4 python command-line-interface python-unittest python-click

我正在尝试通过使用Click 包实现的命令来测试异常的引发。

这是我的命令:

@click.option(
    '--bucket_name',
...)
@click.option(
    '--group_id',
...)
@click.option(
    '--artifact_id',
...)
@click.option(
    '--version',
...)
@click.option(
    '--artifact_dir',
    required=False,
    default='downloads/artifacts/',
...)
@click.command()
def download_artifacts(
    bucket_name,
    group_id, artifact_id, version,
    artifact_dir
):
    logger.info(
        f"bucket_name: {bucket_name}, "
        f"group_id: {group_id}, "
        f"artifact_id: {artifact_id}, "
        f"version: {version}, "
        f"artifact_dir: {artifact_dir}, "
        )

    if not artifact_dir.endswith('/'):
        raise ValueError(
            "Enter artifact_dir ending with '/' ! artifact_dir: "
            f"{artifact_dir}")
...
Run Code Online (Sandbox Code Playgroud)

这是我的测试代码,assertRaises它不起作用:

def test_download_artifacts_invalid_dir(
        self,
    ):
        runner = CliRunner()
        with self.assertRaises(ValueError):
            result = runner.invoke(
                download_artifacts,
                '--bucket_name my_bucket \
                --group_id gi \
                --artifact_id ai \
                --version 1.0.0 \
                --artifact_dir artifact_dir'.split(),
                input='5')
Run Code Online (Sandbox Code Playgroud)

断言失败,E AssertionError: ValueError not raised而是给出。

我发现了这种测试方式,通过了,但看起来不是很优雅:

def test_download_artifacts_invalid_dir(
        self,
    ):
        runner = CliRunner()
        result = runner.invoke(
            download_artifacts,
            '--bucket_name my_bucket \
            --group_id gi \
            --artifact_id ai \
            --version 1.0.0 \
            --artifact_dir artifact_dir'.split(),
            input='5')
        print(f"result.exception: {result.exception}")
        assert "Enter artifact_dir ending" in str(result.exception)
Run Code Online (Sandbox Code Playgroud)

Ste*_*uch 10

使用 click.CliRunner() 测试异常的两种方法

DOCS中暗示了第一种方法:

基本测试

测试 Click 应用程序的基本功能是CliRunner,它可以将命令作为命令行脚本调用。CliRunner.invoke ()方法独立运行命令行脚本,并将输出捕获为字节和二进制数据。

返回值是一个 [Result] 对象,其中包含捕获的输出数据、退出代码和附加的可选异常

result = runner.invoke(throw_value_error)
assert isinstance(result.exception, ValueError)
Run Code Online (Sandbox Code Playgroud)

第二种方法是设置catch_exceptions=False参数CliRunner.invoke()

runner.invoke(..., catch_exceptions=False)
Run Code Online (Sandbox Code Playgroud)

测试代码

import click.testing
import pytest

@click.command()
def throw_value_error():
    raise ValueError("This is My Message!")

def test_catch_value_error():
    """Read the CliRunner exception report"""
    runner = click.testing.CliRunner()
    result = runner.invoke(throw_value_error)
    assert isinstance(result.exception, ValueError)
    assert 'My Message' in str(result.exception)

def test_throw_value_error():
    """Have the CliRunner not catch my exception"""
    runner = click.testing.CliRunner()
    with pytest.raises(ValueError):
        runner.invoke(throw_value_error, catch_exceptions=False)
Run Code Online (Sandbox Code Playgroud)

检测结果

============================= test session starts ==============================
platform linux -- Python 3.7.7, pytest-6.2.1 -- /usr/bin/python
collecting ... collected 2 item

tests/test_api_authz.py::test_catch_value_error PASSED                   [ 50%]
tests/test_api_authz.py::test_throw_value_error PASSED                   [100%]

============================== 2 passed in 0.05s ===============================
Run Code Online (Sandbox Code Playgroud)