Il modo comune di scherno l'oggetto logger (si veda la risposta di splendida cap Simeone Visser) è un po 'complicato in quanto richiede che il test deridi la registrazione in tutti i posti in cui è stato eseguito. Questo è scomodo se la registrazione proviene da più di un modulo o è in codice che non possiedi. Se il modulo viene generato dal nome delle modifiche, interromperà i test.
Lo splendido pacchetto "testfixtures" include strumenti per aggiungere un gestore di registrazione che cattura tutti i messaggi di log generati, indipendentemente da dove provengano. I messaggi catturati possono essere successivamente interrogati dal test. Nella sua forma più semplice:
Supponendo codice sottoposto a test, che registra:
import logging
logger = logging.getLogger()
logger.info('a message')
logger.error('an error')
Un test per questo sarebbe:
from testfixtures import LogCapture
with LogCapture() as l:
call_code_under_test()
l.check(
('root', 'INFO', 'a message'),
('root', 'ERROR', 'an error'),
)
La parola 'root' indica la registrazione è stato inviato tramite un logger creato utilizzando logging.getLogger()
(cioè senza argomenti). Se si passa un argomento a getLogger (__name__
è convenzionale), tale argomento verrà utilizzato al posto di "root".
Al test non interessa quale modulo ha creato la registrazione. Potrebbe essere un sottomodulo chiamato dal nostro codice sotto test, incluso il codice di terze parti.
Il test asserisce sul messaggio di registro effettivo che è stato generato, al contrario della tecnica di derisione, che asserisce sugli argomenti che sono stati passati. Queste differenze saranno diverse se la chiamata logging.info utilizza stringhe di formattazione '% s' con argomenti aggiuntivi che non ti espandi (ad esempio usa logging.info('total=%s', len(items))
invece di logging.info('total=%s' % len(items))
, che dovresti. Non è un lavoro aggiuntivo e consente ipotetici futuri servizi di aggregazione di logging come "Sentry" per funzionare correttamente - possono vedere che "total = 12" e "total = 43" sono due istanze dello stesso messaggio di log.Questo è il motivo per cui pylint mette in guardia sull'ultima forma della chiamata logging.info
.)
LogCapture include servizi per il filtraggio dei registri e simili. Il pacchetto "testfixtures" dei genitori, scritto da Chris Withers, un altro splendido compagno, include molti altri strumenti di test utili.La documentazione è qui: http://pythonhosted.org/testfixtures/logging.html
Piccola cosa ... da Python 3.3 in poi l'importazione per il test dovrebbe essere 'from unittest.mock import patch'. Altrimenti, ancora bene! –
@Simeon Visser come fare con il mock, se all'interno della chiamata di registrazione, stiamo passando un'eccezione? Qualcosa come "logger.exception (KeyError ('test'))" In questo modo "assert_called_with" non lo catturerà poiché immagino che i riferimenti per gli oggetti KeyError siano diversi. (cioè non sono più semplici stringhe). – SpiXel
@SpiXel: puoi vedere il contenuto di mock_logger.error.call_args_list e fare asserzioni sugli argomenti. Ad esempio, sarebbe qualcosa di simile: "assert" Test "in str (my_logging_argument)' o "self.assertEqual (str (mio_logging_argument)," KeyError ('test') "'. Vedi: https: //docs.python .org/3/library/unittest.mock.html # unittest.mock.Mock.call_args_list –