Тестирование исключений команды щелчка Python

#python #command-line-interface #python-unittest #python-click

#python #интерфейс командной строки #python-unittest #python-щелчок

Вопрос:

Я пытаюсь протестировать создание исключений с помощью команды, реализованной с помощью пакета 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}")
...
 

Это мой тестовый код, 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)
 

Ответ №1:

Два способа проверки исключений с помощью click.CliRunner()

Первый метод указан в ДОКУМЕНТАХ:

Базовое тестирование

Основной функциональностью для тестирования приложений Click является CliRunner, который может вызывать команды в виде сценариев командной строки. Метод CliRunner.invoke() запускает сценарий командной строки изолированно и фиксирует выходные данные как в виде байтов, так и в виде двоичных данных.

Возвращаемое значение представляет собой объект [Result], к которому прикреплены захваченные выходные данные, код выхода и необязательное исключение.

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

Второй метод заключается в установке catch_exceptions=False параметра на CliRunner.invoke()

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