为Python记录器格式化输出编写单元测试

时间:2012-06-22 23:20:46

标签: python unit-testing logging

你怎么能在Python中编写一个单元测试来测试记录器的输出确实是你期望的格式(即通过调用logging.basicConfig()来设置)?我正在考虑使用自定义StreamHandler并使用're'库,但它看起来不像传递给StreamHandler.emit()的LogRecord可以给我输出的字符串。

2 个答案:

答案 0 :(得分:6)

从文档(http://packages.python.org/testfixtures/logging.html):

  

为了帮助解决这个问题,TestFixtures允许您轻松捕获对Python日志记录框架的调用输出,并确保它们符合预期。根据您正在编写的测试类型,有三种不同的技术。

     
      
  1. The context manager
  2.   
  3. The decorator
  4.   
  5. The manual usage
  6.   

示例包含在文档中。缩短版本如下。

上下文管理器

>>> import logging
>>> from testfixtures import LogCapture
>>> with LogCapture() as l:
...     logger = logging.getLogger()
...     logger.info('a message')
...     logger.error('an error')

之后,您可以检查日志是否相等:

>>> l.check(
...     ('root', 'INFO', 'a message'),
...     ('root', 'ERROR', 'another error'),
...     )
Traceback (most recent call last):
 ...
AssertionError: Sequence not as expected:

same:
(('root', 'INFO', 'a message'),)

first:
(('root', 'ERROR', 'another error'),)

second:
(('root', 'ERROR', 'an error'),)

装饰者

与之前类似,但适用于特定功能:

from testfixtures import log_capture

@log_capture()
def test_function(l):
    logger = logging.getLogger()
    logger.info('a message')
    logger.error('an error')

    l.check(
        ('root', 'INFO', 'a message'),
        ('root', 'ERROR', 'an error'),
        )

手动使用

>>> from testfixtures import LogCapture
>>> l = LogCapture()

之后您还可以“检查”日志:

>>> l.check(('root', 'INFO', 'a message'))
<...>

编辑:要访问特定日志并以自定义方式对其进行分析,您只需迭代l.records(其中l只是LogCapture的实例)并访问每个属性(例如。msg包含发送给记录器的消息,levelname包含级别的代号,还有很多其他属性。)

答案 1 :(得分:0)

如果只想使用标准库,此解决方案可能会有所帮助。它基于unittestmock库。

例如,如果您有script.py并具有以下内容。

logger = logging.getLogger(__name__)

def log_something():
    logger.debug("something")

您可以为此编写一个测试。

import unittest

import mock

from script import log_something

@mock.patch("script.logger")
def test_function(mock_log):
    log_something()

    assertTrue(
        "something" in mock_log.debug.call_args_list[0][0][0]
    )

这是使用mock库中的call_args_list

最后解释[0][0][0]

  1. call_args_listcall对象的列表,看起来像这样的[call("something")]。因此,第一个[0]返回第一个call对象。

  2. 第二个[0]返回call对象的参数元组。看起来像这样("something",)

  3. 第三[0]返回在我们的情况下给logger的第一个参数。因此,最后一个字符串将仅为"something"