如何测试Django测试用例中是否记录了某个日志消息?

Kry*_*ski 21 python testing django unit-testing python-unittest

我想确保我的代码中的某个条件导致将日志消息写入django日志.我如何使用Django单元测试框架?

有没有我可以检查已记录消息的地方,类似于我如何查看已发送的电子邮件?我的单元测试延伸django.test.TestCase.

Sim*_*ser 36

使用该mock模块模拟日志记录模块或记录器对象.完成后,检查调用日志记录函数的参数.

例如,如果您的代码如下所示:

import logging

logger = logging.getLogger('my_logger')

logger.error("Your log message here")
Run Code Online (Sandbox Code Playgroud)

它看起来像:

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")
Run Code Online (Sandbox Code Playgroud)

  • 小东西......从Python 3.3开始,测试的导入应该是`来自unittest.mock import patch`.否则,还是不错的! (3认同)
  • @SpiXel:您可以查看mock_logger.error.call_args_list的内容并对参数进行断言.例如,它类似于:`str(my_logging_argument)中的`assert'或`self.assertEqual(str(my_logging_argument),'KeyError('test')"`.请参阅:https://docs.python .ORG/3 /库/ unittest.mock.html#unittest.mock.Mock.call_args_list (3认同)

man*_*anu 9

您还可以使用assertLogsdjango.test.TestCase

当你编码是

import logging

logger = logging.getLogger('my_logger')

def code_that_throws_error_log():
    logger.error("Your log message here")
Run Code Online (Sandbox Code Playgroud)

这是测试代码。

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
    )
Run Code Online (Sandbox Code Playgroud)

这样可以避免仅为日志打补丁。

  • 注意:从python 3.4开始,unittest提供assertLogs():https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertLogs (3认同)
  • `django.test.TestCase.assertLogs` 由 Python 3.4+ 中的 `unittest.TestCase.assertLogs` 提供,并且不是 Python 2.7、FWIW 的 polyfill。下面的 `patch_logger` 实用程序是 Python 2.7 用户的唯一选择。见 https://github.com/django/django/pull/9905 (3认同)

Jon*_*ley 6

模拟记录器对象的常见方法(参见精彩的Simeon Visser的答案)有点棘手,因为它需要测试来模拟它所做的所有地方的记录.如果日志记录来自多个模块,或者是您不拥有的代码,则这很尴尬.如果日志记录来自更改名称,那么它将破坏您的测试.

精彩的' testfixtures '软件包包括添加日志记录处理程序的工具,该处理程序捕获所有生成的日志消息,无论它们来自何处.稍后可以通过测试询问捕获的消息.最简单的形式:

假设代码测试,记录:

import logging
logger = logging.getLogger()
logger.info('a message')
logger.error('an error')
Run Code Online (Sandbox Code Playgroud)

对此的测试将是:

from testfixtures import LogCapture
with LogCapture() as l:
    call_code_under_test()
l.check(
    ('root', 'INFO', 'a message'),
    ('root', 'ERROR', 'an error'),
)
Run Code Online (Sandbox Code Playgroud)

"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


mar*_*dev 6

Django 有一个很好的上下文管理器函数,称为patch_logger.

from django.test.utils import patch_logger
Run Code Online (Sandbox Code Playgroud)

然后在您的测试用例中:

with patch_logger('logger_name', 'error') as cm:
    self.assertIn("Error message", cm)
Run Code Online (Sandbox Code Playgroud)

在哪里:

  • logger_name 是记录器名称(废话)
  • error 是日志级别
  • cm 是所有日志消息的列表

更多细节:

https://github.com/django/django/blob/2.1/django/test/utils.py#L638

它应该适用于 django < 2.0,独立于 python 版本(只要它受 dj 支持)

  • 没有这方面的官方文档。当我真的很无聊时,查看 django utils 源代码只是我的一个习惯..;) (3认同)