测试 Python 单击命令异常

Testing Python Click Command Exceptions

我正在尝试通过 Click package.

实现的命令来测试异常的引发

这是我的命令:

@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}")
...

这是我的 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')

断言失败,而是给出 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)

使用 click.CliRunner()

测试异常的两种方法

第一种方法已在 DOCS:

中提示

Basic Testing

The basic functionality for testing Click applications is the CliRunner which can invoke commands as command line scripts. The CliRunner.invoke() method runs the command line script in isolation and captures the output as both bytes and binary data.

The return value is a [Result] object, which has the captured output data, exit code, and optional exception attached.

result = runner.invoke(throw_value_error)
assert isinstance(result.exception, ValueError)

第二种方法是在CliRunner.invoke()

上设置catch_exceptions=False参数
runner.invoke(..., catch_exceptions=False)

测试代码

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)

测试结果

============================= 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 ===============================