Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用mock获取所有日志输出_Python_Unit Testing_Logging_Mocking - Fatal编程技术网

Python 使用mock获取所有日志输出

Python 使用mock获取所有日志输出,python,unit-testing,logging,mocking,Python,Unit Testing,Logging,Mocking,我想用mock获得所有日志输出。我找了,但是 只找到显式模拟logging.info或logging.warn的方法 我需要所有输出,不管设置了什么日志记录级别 def test_foo(): def my_log(...): logs.append(...) with mock.patch('logging.???', my_log): ... 在我们的图书馆中,我们使用: import logging logger=logging.getLogg

我想用mock获得所有日志输出。我找了,但是 只找到显式模拟logging.info或logging.warn的方法

我需要所有输出,不管设置了什么日志记录级别

def test_foo():

   def my_log(...):
      logs.append(...)

   with mock.patch('logging.???', my_log):
        ...
在我们的图书馆中,我们使用:

import logging
logger=logging.getLogger(__name__)

def foo():
    logger.info(...)
我找到了这个解决方案:

def test_foo(self):

    logs=[]

    def my_log(self, *args, **kwargs):
        logs.append((args, kwargs))

    with mock.patch('logging.Logger._log', my_log):
        ...
pytest
如果您使用
pytest
编写测试,请查看一个名为
caplog
的整洁装置,它将为您捕获日志记录。它捕获所有发出的日志记录,然后您可以通过
caplog.records
list访问这些记录。每个元素都是
logging.LogRecord
的一个实例,因此您可以轻松访问其中任何一个元素。例如:

#spam.py
导入日志记录
logger=logging.getLogger(_名称__)
def foo():
logger.info('bar'))
#tests.py
导入日志记录
从垃圾邮件导入foo
def测试_foo(caplog):
foo()
断言len(caplog.records)==1
记录=下一个(iter(caplog.records))
assert record.message=='bar'
assert record.levelno==logging.INFO
assert record.module==“垃圾邮件”
#等
安装 该装置最初是在名为
pytestcapturelog
pytest
插件中引入的,现在该插件已被放弃。幸运的是,最近它得到了一个相当不错的fork,名为
pytestcatchlog
。因此,如果您使用的是最新版本的
pytest
,那么您已经可以使用了;对于较旧版本的
pytest

文件 目前,
pytest
没有为
caplog
装置提供任何文档(或者至少我找不到任何文档),因此您可以参考
pytest catchlog

普通
unittest
如果
pytest
不是一个选项,我根本不会修补
logging
——您只需添加一个自定义处理程序,它将记录所有传入的日志。一个小例子:

# utils.py

import logging


class RecordsCollector(logging.Handler):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.records = []

    def emit(self, record):
        self.records.append(record)


# tests.py

import logging
import unittest
from utils import RecordsCollector
from spam import foo


class SpamTests(unittest.TestCase):

    def setUp(self):
        self.collector = RecordsCollector()
        logging.getLogger('spam').addHandler(self.collector)

    def tearDown(self):
        logging.getLogger('spam').removeHandler(self.collector)

    def test_foo(self):
        foo()
        # same checks as in the example above
        self.assertEqual(len(self.collector.records), 1)
        record = next(iter(self.collector.records))
        self.assertEqual(record.message, 'bar')
        self.assertEqual(record.levelno, logging.INFO)
        self.assertEqual(record.module, 'spam')


if __name__ == '__main__':
    unittest.main()
然后,您可以扩展自定义处理程序并实现所需的任何逻辑,例如在将日志级别映射到记录列表的
dict
中收集记录,或者添加
contextmanager
实现,以便您可以在测试中开始和停止捕获记录:

from contextlib import contextmanager

@contextmanager
def record_logs():
    collector = RecordsCollector()
    logging.getLogger('spam').addHandler(collector)
    yield collector
    logging.getLogger('spam').removeHandler(collector)


def test_foo(self):
    with utils.record_logs() as collector:
        foo()
        self.assertEqual(len(collector.records), 1)
标准库 自Python3.4以来,电池的unittest功能一直在改进。在不使用
logger
level
参数的情况下使用时,它会捕获所有日志记录(抑制现有的处理程序)。以后可以从上下文管理器的
记录
属性访问记录的条目。文本输出字符串存储在
output
列表中

import logging
import unittest


class TestLogging(unittest.TestCase):

    def test(self):
        with self.assertLogs() as ctx:
            logging.getLogger('foo').info('message from foo')
            logging.getLogger('bar').info('message from bar')
        print(ctx.records)
龙卷风 对于Python 2,我通常使用Tornado的。它是自包含的,适用于普通Python代码。它实际上比stdlib的解决方案更优雅,因为它不是几个类,
ExpectLog
只是一个普通的类。但它缺少一些功能,包括访问录制的条目,所以通常我也会对其进行一些扩展,比如:

class ExpectLog(logging.Filter):

    def __init__(self, logger, regex, required=True, level=None):
        if isinstance(logger, basestring):
            logger = logging.getLogger(logger)
        self.logger = logger
        self.orig_level = self.logger.level
        self.level = level
        self.regex = re.compile(regex)
        self.formatter = logging.Formatter()
        self.required = required
        self.matched = []
        self.logged_stack = False

    def filter(self, record):
        if record.exc_info:
            self.logged_stack = True
        message = self.formatter.format(record)
        if self.regex.search(message):
            self.matched.append(record)
            return False
        return True

    def __enter__(self):
        self.logger.addFilter(self)
        if self.level:
            self.logger.setLevel(self.level)
        return self

    def __exit__(self, typ, value, tb):
        self.logger.removeFilter(self)
        if self.level:
            self.logger.setLevel(self.orig_level)
        if not typ and self.required and not self.matched:
            raise Exception("did not get expected log message")
然后你可以做如下的事情:

class TestLogging(unittest.TestCase):

    def testTornadoself):
        logging.basicConfig(level = logging.INFO)

        with ExpectLog('foo', '.*', required = False) as ctxFoo:
            with ExpectLog('bar', '.*', required = False) as ctxBar:
                logging.getLogger('foo').info('message from foo')
                logging.getLogger('bar').info('message from bar')
        print(ctxFoo.matched)
        print(ctxBar.matched)
但是,请注意,对于过滤器方法,当前日志记录级别很重要(可以使用
level
参数覆盖),并且每个感兴趣的记录器都需要一个过滤器。你可以按照这种方法做一些更适合你的事情

更新
另外,Python2还有一个backport,它有
assertLogs

模块testfixtures有一个类来处理这个问题:

>>> 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'),)

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

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

来源:

我认为这个解决方案太复杂了。好消息!到目前为止,我使用的是旧版本,但这是升级的原因。谢谢你的回答。很高兴我能帮忙!为了完整起见,我还为
unittest
-测试添加了一个解决方案。尽管该提案主要模仿了
caplog
fixture的实现……pytest-
caplog
-fixture的文档可以在这里找到:感谢
unittest
-示例。这对我来说非常有效。我最喜欢stdlib解决方案。到目前为止,我仍然固定在Python2.7上,但迟早我们会在那里。感谢您的回答。更新了答案,并引用了unittest2 backport,因此您在整个过程中使用了相同的方法。nice,backport。是的,我们仍然使用Python2.7。我问自己为什么Python世界中有这么多选择。有时这很好,有时令人困惑:-)在这种情况下:unittest2与pytest。假设
log.records
的顺序与记录日志的顺序相同,安全吗?举例来说,它不是按级别排序的?我认为可以这样假设<目标记录器的代码>处理程序临时更改为
\u CapturingHandler
的实例,其中它只是将原始日志记录附加到两个列表中,将原始日志记录附加到一个,并将其格式化表示形式附加到另一个。