#python #logging #mocking
#python #ведение журнала #издевательство
Вопрос:
Не мог бы кто-нибудь из вас объяснить, почему следующее assert_called_with
не работает, пожалуйста?
Согласно информации, напечатанной pytest ‘Ожидаемый вызов’ равен ‘Фактическому вызову’, поэтому я предполагаю, что некоторая информация теряется, когда pytest преобразует ее в str для печати на терминале.
Какой инструмент вы бы порекомендовали для его отладки?
Программа:
import logging
from mock import patch
logger = logging.getLogger(__name__)
logger.addHandler(logging.StreamHandler())
def func():
try:
raise ValueError("error XYZ")
except ValueError as err:
logger.error(err)
def test_func():
with patch('logging.Logger.error') as log:
func()
log.assert_called_with(ValueError("error XYZ"))
Выполнение:
$ pytest prog.py
=========================================================================== test session starts ===========================================================================
platform linux2 -- Python 2.7.15 , pytest-4.2.0, py-1.7.0, pluggy-0.8.1
rootdir: /tmp, inifile:
collected 1 item
prog.py F [100%]
================================================================================ FAILURES =================================================================================
________________________________________________________________________________ test_func ________________________________________________________________________________
def test_func():
with patch('logging.Logger.error') as log:
func()
> log.assert_called_with(ValueError("error XYZ"))
prog.py:16:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/home/ebajgrz/py27/local/lib/python2.7/site-packages/mock/mock.py:937: in assert_called_with
six.raise_from(AssertionError(_error_message(cause)), cause)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
value = AssertionError("Expected call: error(ValueError('error XYZ',))nActual call: error(ValueError('error XYZ',))",), from_value = None
def raise_from(value, from_value):
> raise value
E AssertionError: Expected call: error(ValueError('error XYZ',))
E Actual call: error(ValueError('error XYZ',))
/home/ebajgrz/py27/local/lib/python2.7/site-packages/six.py:737: AssertionError
======================================================================== deprecated python version ========================================================================
You are using Python 2.7.15, which will no longer be supported in pytest 5.0
For more information, please read:
https://docs.pytest.org/en/latest/py27-py34-deprecation.html
======================================================================== 1 failed in 0.28 seconds ============================
Я понимаю, что в pytest есть caplog, и можно реализовать test следующим образом, но я хотел бы понять, почему первый метод не работает.
def test_func2(caplog):
func()
assert 'error XYZ' in caplog.text
Ответ №1:
Это потому, что mock
проверяет объект, который был передан, на соответствие ожидаемому объекту. Это разные объекты, поэтому утверждение не выполняется. Например, откройте оболочку python и попробуйте это:
>>> ValueError('bees') == ValueError('bees')
False
Они не равны, потому что это разные объекты. Но попробуйте это:
>>> str(ValueError('bees')) == str(ValueError('bees'))
True
Это потому, что строки неизменяемы. Теперь, если вы измените свой исходный вызов журнала на этот: logger.error(str(err))
и измените свое утверждение на: log.assert_called_with(str(ValueError("error XYZ")))
, я думаю, вы преодолеете эту первую ошибку.
Не имеет отношения к этому, и еще одно предложение по протоколированию исключений: возможно, вы захотите записать дополнительную информацию в подобный блок except. Что-то вроде этого:
logger.error('Error raised doing something: %s', str(err), exc_info=True)