2011-09-19 5 views
36

Sto usando PyDev per lo sviluppo e il test unitario della mia applicazione Python. Per quanto riguarda il test delle unità, tutto funziona in modo ottimale per il fatto che i contenuti registrati su qualsiasi registrazione. Il logger non viene catturato dall'uscita "catturata" di PyDev.PyDev unittesting: come catturare il testo registrato su un logging.Logger in "Output catturato"

ho già avanti tutto collegato sullo standard output come questo:

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

Tuttavia la "Uscita catturati" non visualizza roba registrato al logger.

Ecco un esempio 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") 

L'uscita della console è:

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 

Ma la USCITA catturata per il test è:

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

Qualcuno sa come catturare tutto è registrato su un logging.Logger durante l'esecuzione di questo test?

risposta

32

Il problema è che il corridore unittest sostituisce sys.stdout/sys.stderr prima che il test si avvia, e il StreamHandler sta ancora scrivendo a quello originale sys.stdout.

Se si assegna il "corrente" sys.stdout al gestore, dovrebbe funzionare (vedere il codice di seguito).

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") 

Anche se, un approccio migliore sarebbe l'aggiunta/rimozione del gestore durante il test:

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

Per completezza: ho bisogno di questo reindirizzamento per tutti i miei unit test. La soluzione migliore per me è aggiungere il nuovo gestore nel metodo setUp e rimuoverlo nel metodo tearDown. – gecco

+1

Ottima risposta, ho [esteso] (http://stackoverflow.com/a/15969985/321973) a un '__metaclass__', quindi un setUp e' tearDown' inclusi includono automaticamente questo –

+1

Perché è meglio aggiungere/rimuovere i gestori invece di tenerli al di fuori del test case? – mlt

14

sono cresciuto stanco di dover aggiungere manualmente Fabio's great code a tutti setUp s, così ho sottoclasse unittest.TestCase con un po '__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) 

Ora il tuo test case può semplicemente ereditare da LoggedTestCase, ovvero class TestCase(LoggedTestCase) anziché class TestCase(unittest.TestCase) e il gioco è fatto. In alternativa, è possibile aggiungere la riga __metaclass__ e definire lo logger nel test o in un LogThisTestCase leggermente modificato.

+1

Uso impressionante di '__metaclass__' – Randy

+1

@Randy Grazie, dopo aver letto [questa grande spiegazione di' __metaclass__'] (http://stackoverflow.com/a/6581949/321973) ho solo _had_ usarlo ... –

+0

Ho trovato sto leggendo la stessa risposta la scorsa settimana e lo ha già inserito nella mia base di codice. – Randy

5

Io suggerirei di utilizzare un LogCapture e test che realmente stanno registrando quello che ci si aspetta di essere la registrazione:

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

+0

@Wooble - sorry , spostato i documenti in RTD, così ora sono a: http://testfixtures.readthedocs.org/en/latest/ –

+0

Mi è piaciuto il fatto che ci sia un pacchetto che si prende cura di esso, quindi non è necessario scrivere codice ovviamente per un problema comune. Ma sfortunatamente mi sono imbattuto in problemi di UTF quando usavo "print (l)" che non ho, quando uso il logger di solito. Quindi il mio approccio era "sys.stdout.buffer.write (l .__ str __(). Encode ('utf8'))" invece. Funziona bene, quindi ho voluto condividerlo. –

+0

@IwanLD - Suggerirei di usare 'LogCapture(). Check()' al giorno d'oggi. Come mai non potresti usarlo? –

1

mi sono imbattuto in questo problema anche. Ho finito con la sottoclasse di StreamHandler e ho sovrascritto l'attributo stream con una proprietà che ottiene sys.stdout. In questo modo, il gestore utilizzerà lo stream che l'unittest.TestCase ha scambiato in sys.stdout:

class CapturableHandler(logging.StreamHandler): 

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

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

È possibile quindi impostare il gestore di registrazione prima di eseguire i test in questo modo (questo aggiungerà il gestore personalizzato al logger principale):

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

Se, come me, avete le vostre prove in moduli separati, si può solo mettere una riga dopo le importazioni di ciascun modulo di unit test che farà in modo di registrazione è la messa a punto prima che i test vengono eseguiti:

import logutil 

logutil.setup_capturable_logging() 

questo potrebbe non essere il più pulito appr oach, ma è piuttosto semplice e ha funzionato bene per me.

Problemi correlati