自定义pytest中特定异常的错误消息

时间:2016-04-09 18:42:47

标签: python 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 ======================

2 个答案:

答案 0 :(得分:1)

如果您的目标是使堆栈跟踪更容易阅读,那么您可以使用以下代码块输出自定义错误消息。此自定义错误消息显示在堆栈跟踪的末尾,因此您不需要向上滚动:

with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"):
    pass
--> Failed: Expecting ZeroDivisionError

来源:pytest's documentation。所以,不是制作一个插件,你可以通过像grep这样的东西来管道pytest的输出,并过滤掉栈跟踪中无用的部分。

根据我在文档中阅读的内容,您使用正确的pytest装饰器和钩子函数(pytest_exception_interact)。但是,可能不需要对错误进行类型检查。 This section of the documentation表示&#34;只有在引发的异常不是像skip.Exception这样的内部异常时才会调用此挂钩。&#34;

答案 1 :(得分:0)

我实现了类似的目标(来自pytest的自定义错误报告),采用与Mikaeil提出的方法类似的方法 - 拦截输出,解析它,然后应用我需要的任何过滤器。

以下是步骤:

  1. 我从脚本运行pytest,因此我可以控制执行上下文和配置,使其保持一致。
  2. 我用它来配置pytest以json格式输出错误到日志文件。
  3. 我还将stdout和stderr重定向到另一个日志文件(隐藏它)。
  4. 如果pytest返回非零结果,我会解析json文件并将结果传递给&#39;构建监视器&#39; class,然后输出到stdout和HTML。
  5. 我为所有静态分析工具,单元测试,C编译器等使用相同的构建监视器类...因此构建过程可以报告错误(HTML和控制台)和统一的方式。< / p>

    pytest的相关文件在这里: https://github.com/wtpayne/hiai/blob/master/a3_src/h70_internal/da/check/pytest.py

    此处还有一个pytest插件来控制覆盖率报告: https://github.com/wtpayne/hiai/blob/master/a3_src/h70_internal/da/check/pytest_da.py

    输出格式化类在这里: https://github.com/wtpayne/hiai/blob/master/a3_src/h70_internal/da/monitor/console_reporter.py