PyDev unittesting: jak przechwycić tekst zalogowany do logowania.Logger in " przechwycone wyjście"

Używam PyDev do rozwoju i testowania jednostkowego mojej aplikacji Pythona. Jeśli chodzi o testy jednostkowe, wszystko działa świetnie, ponieważ treść logowana jest do dowolnego logowania. Logger nie jest przechwytywany przez" przechwycone wyjście " PyDev.

Już przesyłam wszystko co zalogowane do standardowego wyjścia tak:

import sys
logger = logging.getLogger()
logger.level = logging.DEBUG
logger.addHandler(logging.StreamHandler(sys.stdout))

Mimo to "przechwycone wyjście"nie wyświetla rzeczy zapisanych do rejestratorów.

Tutaj przykład unittest-script: test.py

import sys
import unittest
import logging

logger = logging.getLogger()
logger.level = logging.DEBUG
logger.addHandler(logging.StreamHandler(sys.stdout))

class TestCase(unittest.TestCase):
    def testSimpleMsg(self):
        print("AA")
        logging.getLogger().info("BB")

Wyjście konsoli to:

Finding files... done.
Importing test modules ... done.

testSimpleMsg (itf.lowlevel.tests.hl7.TestCase) ... AA
2011-09-19 16:48:00,755 - root - INFO - BB
BB
ok

----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

Ale przechwycone Wyjście do testu to:

======================== CAPTURED OUTPUT =========================
AA

Czy ktoś wie jak przechwycić wszystko jest rejestrowane na logging.Logger podczas wykonywania tego testu?

Author: P̲̳x͓L̳, 2011-09-19

4 answers

Problem polega na tym, że unittest runner zastępuje sys.stdout/sys.stderr przed rozpoczęciem testów, a StreamHandler nadal pisze do oryginału sys.stdout.

Jeśli przypisujesz' current ' sys.stdout do obsługi, powinno to działać(patrz kod poniżej).

import sys
import unittest
import logging

logger = logging.getLogger()
logger.level = logging.DEBUG
stream_handler = logging.StreamHandler(sys.stdout)
logger.addHandler(stream_handler)

class TestCase(unittest.TestCase):
    def testSimpleMsg(self):
        stream_handler.stream = sys.stdout
        print("AA")
        logging.getLogger().info("BB")

Chociaż lepszym podejściem byłoby dodanie / usunięcie obsługi podczas testu:

import sys
import unittest
import logging

logger = logging.getLogger()
logger.level = logging.DEBUG

class TestCase(unittest.TestCase):
    def testSimpleMsg(self):
        stream_handler = logging.StreamHandler(sys.stdout)
        logger.addHandler(stream_handler)
        try:
            print("AA")
            logging.getLogger().info("BB")
        finally:
            logger.removeHandler(stream_handler)
 42
Author: Fabio Zadrozny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-28 20:15:48

Zmęczyło mnie ręczne dodawanie wspaniałego kodu do wszystkich setUp s, Więc podklasowałem unittest.TestCase z jakimś __metaclass__ ing:

class LoggedTestCase(unittest.TestCase):
    __metaclass__ = LogThisTestCase
    logger = logging.getLogger("unittestLogger")
    logger.setLevel(logging.DEBUG) # or whatever you prefer

class LogThisTestCase(type):
    def __new__(cls, name, bases, dct):
        # if the TestCase already provides setUp, wrap it
        if 'setUp' in dct:
            setUp = dct['setUp']
        else:
            setUp = lambda self: None
            print "creating setUp..."

        def wrappedSetUp(self):
            # for hdlr in self.logger.handlers:
            #    self.logger.removeHandler(hdlr)
            self.hdlr = logging.StreamHandler(sys.stdout)
            self.logger.addHandler(self.hdlr)
            setUp(self)
        dct['setUp'] = wrappedSetUp

        # same for tearDown
        if 'tearDown' in dct:
            tearDown = dct['tearDown']
        else:
            tearDown = lambda self: None

        def wrappedTearDown(self):
            tearDown(self)
            self.logger.removeHandler(self.hdlr)
        dct['tearDown'] = wrappedTearDown

        # return the class instance with the replaced setUp/tearDown
        return type.__new__(cls, name, bases, dct)

Teraz twój przypadek testowy może po prostu dziedziczyć z LoggedTestCase, tzn. class TestCase(LoggedTestCase) zamiast class TestCase(unittest.TestCase) i gotowe. Alternatywnie możesz dodać linię __metaclass__ i zdefiniować logger W teście lub nieco zmodyfikowaną LogThisTestCase.

 14
Author: Tobias Kienzler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:50

Sugerowałbym użycie LogCapture i testowanie, że naprawdę rejestrujesz to, czego oczekujesz:

Http://testfixtures.readthedocs.org/en/latest/logging.html

 5
Author: Chris Withers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-06 14:58:09

Też natknąłem się na ten problem. Skończyło się na podklasowaniu StreamHandler i nadpisaniu atrybutu stream właściwością, która dostaje sys.stdout. W ten sposób handler użyje strumienia, który jest unittest.TestCase zamienił się na sys.stdout:

class CapturableHandler(logging.StreamHandler):

    @property
    def stream(self):
        return sys.stdout

    @stream.setter
    def stream(self, value):
        pass

Możesz następnie skonfigurować obsługę logowania przed uruchomieniem testów w ten sposób (spowoduje to dodanie niestandardowej obsługi do dziennika głównego):

def setup_capturable_logging():
    if not logging.getLogger().handlers:
        logging.getLogger().addHandler(CapturableHandler())

Jeśli, podobnie jak ja, masz swoje testy w oddzielnych modułach, możesz po prostu umieścić linię po import każdego modułu testów jednostkowych, który upewni się, że logowanie jest ustawione przed uruchomieniem testów:

import logutil

logutil.setup_capturable_logging()

To może nie jest najczystsze podejście, ale jest dość proste i działa dobrze dla mnie.

 1
Author: Narotak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-21 23:31:38