我想确保代码中的某个条件导致将日志消息写入django日志。我将如何使用Django单元测试框架?
是否有可以检查已记录消息的地方,类似于我如何查看已发送的电子邮件?我的单元测试扩展了django.test.TestCase
。
答案 0 :(得分:35)
使用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")
答案 1 :(得分:6)
模拟记录器对象的常用方法(参见精彩的Simeon Visser的答案)有点棘手,因为它需要测试来模拟它所完成的所有位置的记录。如果日志记录来自多个模块,或者是您不拥有的代码,则这很尴尬。如果日志记录来自更改名称,则会破坏您的测试。
精彩的'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'),
)
单词“root”表示日志记录是通过使用logging.getLogger()
创建的记录器发送的(即没有args。)如果您将arg传递给getLogger(__name__
是常规的),则该arg将用来取代'root'。
测试并不关心哪个模块创建了日志记录。它可能是我们的测试代码调用的子模块,包括第三方代码。
测试断言生成的实际日志消息,而不是模拟技术,该模型断言传递的args。如果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'包由Chris Withers编写,另一个出色的小伙伴,包括许多其他有用的测试工具。文档在这里:http://pythonhosted.org/testfixtures/logging.html
答案 2 :(得分:3)
您还可以使用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 :(得分:1)
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
是记录器名称(duh)error
是日志级别cm
是所有日志消息的列表更多详细信息:
https://github.com/django/django/blob/2.1/django/test/utils.py#L638
对于django <2.0,它应该与python版本无关(只要dj支持)
答案 4 :(得分:0)
如果您正在使用测试类,则可以使用以下解决方案:
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
变量中,该变量在每个测试用例中都可通过调用{{1} }。最后,它通过将self.logger
模块放回error
函数来恢复更改。