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

时间:2013-02-04 15:48:34

标签: python django unit-testing testing python-unittest

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

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

5 个答案:

答案 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函数来恢复更改。