Настройка сообщения об ошибке для определенных исключений в pytest
Я пытаюсь написать плагин pytest, чтобы настроить внешний вид определенных исключений - точнее, насмешливые исключения (метод, который, как ожидается, будет вызываться, не был вызван и т.д.), потому что там много бесполезного шума в трассировке тех исключения.
Это то, что у меня есть до сих пор, которое работает, но крайне хакерское:
import pytest
import flexmock
@pytest.hookimpl()
def pytest_exception_interact(node, call, report):
exc_type = call.excinfo.type
if exc_type == flexmock.MethodCallError:
entry = report.longrepr.reprtraceback.reprentries[-1]
entry.style = 'short'
entry.lines = [entry.lines[-1]]
report.longrepr.reprtraceback.reprentries = [entry]
Я думаю, что я делаю все правильно с помощью hookimpl
и проверяю тип исключения с помощью простого оператора if.
Я попытался заменить report.longrepr
на простую строку, которая также работала, но потом я теряю при форматировании (цвета в терминале).
В качестве примера типа вывода, который я хочу сократить, вот ошибка неудачного утверждения:
=================================== FAILURES ====================================
_______________________ test_session_calls_remote_client ________________________
def test_session_calls_remote_client():
remote_client = mock.Mock()
session = _make_session(remote_client)
session.connect()
remote_client.connect.assert_called_once_with()
session.run_action('asdf')
> remote_client.run_action.assert_called_once_with('asdff')
tests/unit/executor/remote_test.py:22:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/opt/python-3.6.3/lib/python3.6/unittest/mock.py:825: in assert_called_once_with
return self.assert_called_with(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_mock_self = <Mock name='mock.run_action' id='139987553103944'>
args = ('asdff',), kwargs = {}, expected = (('asdff',), {})
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x7f51646269d8>
actual = call('asdf'), cause = None
def assert_called_with(_mock_self, *args, **kwargs):
"""assert that the mock was called with the specified arguments.
Raises an AssertionError if the args and keyword args passed in are
different to the last call to the mock."""
self = _mock_self
if self.call_args is None:
expected = self._format_mock_call_signature(args, kwargs)
raise AssertionError('Expected call: %s\nNot called' % (expected,))
def _error_message():
msg = self._format_mock_failure_message(args, kwargs)
return msg
expected = self._call_matcher((args, kwargs))
actual = self._call_matcher(self.call_args)
if expected != actual:
cause = expected if isinstance(expected, Exception) else None
> raise AssertionError(_error_message()) from cause
E AssertionError: Expected call: run_action('asdff')
E Actual call: run_action('asdf')
/opt/python-3.6.3/lib/python3.6/unittest/mock.py:814: AssertionError
====================== 1 failed, 30 passed in 0.28 seconds ======================
Ответы
Ответ 1
Если ваша цель состоит в том, чтобы упростить чтение stacktrace, вы можете использовать приведенный ниже блок кода для вывода пользовательского сообщения об ошибке. Это сообщение об ошибке появляется в конце stacktrace, поэтому вам не нужно прокручивать вверх:
with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"):
pass
--> Failed: Expecting ZeroDivisionError
Источник: документация pytest. Таким образом, вместо создания плагина вы можете вывести вывод pytest через что-то вроде grep и отфильтровать бесполезные части stacktrace.
Основываясь на том, что я прочитал в документации, вы используете правильную декоративную декоративу и функцию крюка (pytest_exception_interact
). Но проверка типа для ошибки может быть ненужной. В этом разделе документации говорится, что "этот hook вызывается только в том случае, если было создано исключение, которое не является внутренним исключением, например skip.Exception".