如何测试是否正确安装了自定义excepthook?

时间:2017-09-19 21:30:18

标签: python testing logging exception-handling pytest

我的应用会记录未处理的例外情况。

# app.py
import logging
import sys

logger = logging.getLogger(__name__)

def excepthook(exc_type, exc_value, traceback):
    exc_info = exc_type, exc_value, traceback
    if not issubclass(exc_type, (KeyboardInterrupt, SystemExit)):
        logger.error('Unhandled exception', exc_info=exc_info)
    sys.__excepthook__(*exc_info)

sys.excepthook = excepthook

def potato():
    logger.warning('about to die...')
    errorerrorerror

if __name__ == '__main__':
    potato()

这些测试通过OK:

# test_app.py
import app
import pytest
import sys
from logging import WARNING, ERROR

def test_potato_raises():
    with pytest.raises(NameError):
        app.potato()

def test_excepthook_is_set():
    assert sys.excepthook is app.excepthook

# for caplog plugin: pip install pytest-catchlog
def test_excepthook_logs(caplog):  
    try:
        whatever
    except NameError as err:
        exc_info = type(err), err, err.__traceback__
    app.excepthook(*exc_info)
    assert caplog.record_tuples == [('app', ERROR, 'Unhandled exception')]
    [record] = caplog.records
    assert record.exc_info == exc_info

但我无法测试未处理的异常日志记录工作:

def test_unhandled_exceptions_logged(caplog):
    try:
        app.potato()
    finally:
        assert caplog.record_tuples == [
            ('app', WARNING, 'about to die...'),
            ('app', ERROR, 'Unhandled exception'),
        ]
        return  # return eats exception

这里有什么问题?我们如何在测试中实际触发app.excepthook

2 个答案:

答案 0 :(得分:1)

Python不会调用sys.excepthook,直到异常实际传播到整个堆栈并且没有更多代码有机会捕获它。这是Python在响应异常时关闭之前发生的最后一件事。

只要您的测试代码仍在堆栈中,sys.excepthook就不会触发。在sys.excepthook之后,实际上可以运行的小代码可能不适合您的测试框架。例如,atexit处理程序仍然可以运行,但到那时测试已经结束。此外,如果您不这样做,您的测试框架可能会捕获异常,因此sys.excepthook无论如何都不会触发。

如果您不想自己致电sys.excepthook,最好的办法是在安装excepthook的情况下启动整个子流程,并验证子流程的行为。

from subprocess import Popen, PIPE

def test_app():
    proc = Popen([sys.executable, 'app.py'], stdout=PIPE, stderr=PIPE)
    stdout, stderr = proc.communicate()
    assert proc.returncode == 1
    assert stdout == b''
    assert stderr.startswith(b'about to die...\nUnhandled exception')

答案 1 :(得分:-2)

pytest可以检查异常消息。你可以这样做:

>>> import pytest
>>> def foo():
...  raise ValueError('Unhandled Exception')
...
>>> with pytest.raises(ValueError) as exc:
...  foo()
...
>>> 'Unhandled Exception' in str(exc)
True
>>> str(exc)
'<stdin>:2: ValueError: Unhandled Exception'
>>>

你可以在一次测试中测试整个事物。无需多个测试功能。