Get all log output using mock

I want to get all the log output using mock. I searched, but only found ways to mock explicitly logging.info or logging.warn.

I need all the output, regardless of the level of logging.

def test_foo():

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

   with mock.patch('logging.???', my_log):
        ...

In our libraries we use the following:

import logging
logger=logging.getLogger(__name__)

def foo():
    logger.info(...)
+4
source share
3 answers

STDLIB

Since Python 3.4, batteries unittesthave assertLogs. When used without arguments logger, levelit captures all logging (suppresses existing handlers). You can later access the recorded records from the recordscontext manager attribute . Text output lines are stored in a list 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)

Tornado

Python 2 Tornado ExpectLog. Python. , stdlib, ExpectLog logging.Filter (, ). , , , :

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

Update

unittest2 backport Python 2, assertLogs.

+1

pytest

pytest, caplog, . , caplog.records. logging.LogRecord, LogRecord. :

# spam.py

import logging
logger=logging.getLogger(__name__)

def foo():
    logger.info('bar')


# tests.py

import logging
from spam import foo

def test_foo(caplog):
    foo()
    assert len(caplog.records) == 1
    record = next(iter(caplog.records))
    assert record.message == 'bar'
    assert record.levelno == logging.INFO
    assert record.module == 'spam'
    # etc

pytest pytest-capturelog, . , pytest-catchlog, pytest==3.3.0. , pytest, ; pytest, pytest-catchlog PyPI.

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)
+3

I found this solution:

def test_foo(self):

    logs=[]

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

    with mock.patch('logging.Logger._log', my_log):
        ...
0
source

Source: https://habr.com/ru/post/1533502/


All Articles