Тестирование исключений Python Click Command

Я пытаюсь протестировать вызов исключений командой, реализованной с помощью пакета 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)
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python.
Некоторые методы, о которых вы не знали, что они существуют в Python.
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
0
2 653
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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 ===============================

Я пытаюсь воспроизвести это, используя встроенные исключения кликов, но pytest видит это только как SystemError<2>. Есть идеи? raise click.UsageError("End datetime must be after start datetime") assert isinstance(result.exception, click.exceptions.UsageError) ``` E AssertionError: assert False E + где False = isinstance(SystemExit(2), <class 'click.exceptions.UsageError'>) E + где SystemExit(2) = <Result SystemExit(2)>.exception ```

yuletide 04.04.2023 03:12

@yuletide Все исключения кликов переводятся в SystemExit, если вы не используете standalone_mode. Один пример: stackoverflow.com/q/34286165/7311767

Stephen Rauch 04.04.2023 16:03

Другие вопросы по теме