Я пытаюсь протестировать вызов исключений командой, реализованной с помощью пакета 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)
Первый способ указан в ДОКУМЕНТАХ:
Базовое тестирование Базовой функциональностью для тестирования приложений 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 ===============================
@yuletide Все исключения кликов переводятся в SystemExit, если вы не используете standalone_mode. Один пример: stackoverflow.com/q/34286165/7311767
Я пытаюсь воспроизвести это, используя встроенные исключения кликов, но 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 ```