Как проверить, зарегистрировано ли какое-либо сообщение журнала в тестовом примере Django?
Я хочу убедиться, что определенное условие в моем коде вызывает запись журнала в журнал django. Как я могу это сделать с помощью модульной системы тестирования Django?
Есть ли место, где я могу проверять зарегистрированные сообщения, аналогично тому, как я могу проверять отправленные электронные письма? Мой unit test расширяет django.test.TestCase
.
Ответы
Ответ 1
Используя модуль mock
для издевательств модуля регистрации или объекта журнала. Когда вы это сделаете, проверьте аргументы, с которыми вызывается функция ведения журнала.
Например, если код выглядит следующим образом:
import logging
logger = logging.getLogger('my_logger')
logger.error("Your log message here")
он выглядел бы так:
from unittest.mock import patch # For python 2.x use from mock import patch
@patch('this.is.my.module.logger')
def test_check_logging_message(self, mock_logger):
mock_logger.error.assert_called_with("Your log message here")
Ответ 2
Вы также можете использовать assertLogs
от django.test.TestCase
При кодировании
import logging
logger = logging.getLogger('my_logger')
def code_that_throws_error_log():
logger.error("Your log message here")
Это тестовый код.
with self.assertLogs(logger='my_logger', level='ERROR') as cm:
code_that_throws_error_log()
self.assertIn(
"ERROR:your.module:Your log message here",
cm.output
)
Это позволяет избежать исправления только для журналов.
Ответ 3
Обычный способ макетирования объекта логгера (см. великолепный ответ симеона Виссера, глава) немного сложен в том смысле, что он требует теста, чтобы макетировать логирование во всех местах, которые он сделал. Это неудобно, если регистрация ведется из более чем одного модуля или в коде, который вам не принадлежит. Если запись в модуль происходит из-за изменения имени, это нарушит ваши тесты.
В великолепный пакетtestfixtures входят инструменты для добавления обработчика ведения журнала, который собирает все сгенерированные сообщения журнала, независимо от того, откуда они приходят. Захваченные сообщения могут быть позже опрошены тестом. В простейшем виде:
Предполагается, что тестируется код, который регистрирует:
import logging
logger = logging.getLogger()
logger.info('a message')
logger.error('an error')
Тестом для этого будет:
from testfixtures import LogCapture
with LogCapture() as l:
call_code_under_test()
l.check(
('root', 'INFO', 'a message'),
('root', 'ERROR', 'an error'),
)
Слово "корень" означает, что запись в журнал была отправлена через регистратор, созданный с использованием logging.getLogger()
(то есть без аргументов.) Если вы передадите arg в getLogger (__name__
является обычным), этот аргумент будет использоваться вместо корня.
Тесту не важно, какой модуль создал логи. Это может быть субмодуль, вызываемый нашим тестируемым кодом, включая сторонний код.
Тест утверждает о фактическом сгенерированном сообщении журнала, в отличие от метода насмешки, который утверждает о переданных аргументах. Они будут отличаться, если в вызове logging.info используются строки формата '% s' с дополнительными аргументами, которые вы не раскрываете сами (например, используйте logging.info('total=%s', len(items))
вместо logging.info('total=%s' % len(items))
, что вам следует. Это не требует дополнительной работы и допускает гипотетическую гипотезу). будущие службы агрегации журналирования, такие как 'Sentry', будут работать правильно - они видят, что "total = 12" и "total = 43" - это два экземпляра одного и того же сообщения журнала. Именно поэтому Pylint предупреждает о последней форме logging.info
позвоните.)
LogCapture включает в себя средства для фильтрации журналов и тому подобное. Его родительский пакет 'testfixtures', написанный Крисом Уизерсом, еще одним замечательным главой, включает в себя множество других полезных инструментов тестирования. Документация здесь: http://pythonhosted.org/testfixtures/logging.html
Ответ 4
В Django есть замечательная функция менеджера контекста, которая называется patch_logger
.
from django.test.utils import patch_logger
тогда в вашем тестовом случае:
with patch_logger('logger_name', 'error') as cm:
self.assertIn("Error message", cm)
где:
logger_name
это имя регистратора (дух)
error
- уровень журнала
cm
- список всех сообщений журнала
Подробнее:
https://github.com/django/django/blob/2.1/django/test/utils.py#L638
Это должно работать так же для django & lt; 2.0, независимо от версии Python (если она поддерживается dj)
Ответ 5
Если вы используете тестовые классы, вы можете использовать следующее решение:
import logger
from django.test import TestCase
class MyTest(TestCase):
@classmethod
def setUpClass(cls):
super(MyTest, cls).setUpClass()
cls.logging_error = logging.error
logging.error = cls._error_log
@classmethod
def tearDownClass(cls):
super(MyTest, cls).tearDownClass()
logging.error = cls.logging_error
@classmethod
def _error_log(cls, msg):
cls.logger = msg
def test_logger(self):
self.assertIn('Message', self.logger)
Этот метод заменяет функцию error
модуля logging
вашим пользовательским методом только для целей тестирования и ставит stdout в переменную cls.logger
, которая доступна в каждом тестовом случае, вызывая self.logger
. В конце он возвращает изменения, помещая error
функцию из модуля logging
назад.