2013-02-04 8 views
12

Voglio garantire che una determinata condizione nel mio codice causi la scrittura di un messaggio di registro nel registro django. Come farei questo con il framework di test dell'unità Django?Come si verifica se un determinato messaggio di registro è registrato in un caso di prova Django?

C'è un posto dove posso controllare i messaggi registrati, in modo simile a come posso controllare le email inviate? Il mio test dell'unità estende django.test.TestCase.

risposta

27

Utilizzo del modulo mock per il controllo del modulo di registrazione o dell'oggetto del logger. Al termine, controlla gli argomenti con cui viene chiamata la funzione di registrazione.

Ad esempio, se si codice simile a questo:

import logging 

logger = logging.getLogger('my_logger') 

logger.error("Your log message here") 

sarebbe simile:

from unittest.mock import patch # For python 2.x use from mock import patch 

@patch('this.is.my.module.logger') 
def test_check_logging_message(self, mock_logger): 
    mock_logger.error.assert_called_with("Your log message here") 
+3

Piccola cosa ... da Python 3.3 in poi l'importazione per il test dovrebbe essere 'from unittest.mock import patch'. Altrimenti, ancora bene! –

+0

@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

+3

@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 –

3

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

+0

Buon lavoro Jonathan! :) –

0

Se stai usando classi di test, è possibile utilizzare seguente soluzione:

import logger 

from django.test import TestCase 


class MyTest(TestCase): 
    @classmethod 
    def setUpClass(cls): 
     super(MyTest, cls).setUpClass() 
     cls.logging_error = logging.error 
     logging.error = cls._error_log 

    @classmethod 
    def tearDownClass(cls): 
     super(MyTest, cls).tearDownClass() 
     logging.error = cls.logging_error 

    @classmethod 
    def _error_log(cls, msg): 
     cls.logger = msg 

    def test_logger(self): 
     self.assertIn('Message', self.logger) 

Questo metodo sostituisce error funzione del logging modulo con il vostro metodo personalizzato solo a scopo di test e mettere in stdout Variabile cls.logger che è disponibile in ogni caso di test chiamando lo self.logger. Alla fine ripristina le modifiche inserendo la funzione error dal modulo logging indietro.

+0

Penso che dovresti usare la funzione 'mock.patch' invece di' setUpClass' e 'tearDownClass'. –

1

è anche possibile utilizzare assertLogs da django.test.TestCase

Quando il codice è

import logging 

logger = logging.getLogger('my_logger') 

def code_that_throws_error_log(): 
    logger.error("Your log message here") 

Questo è il codice di prova.

Ciò consente di evitare le patch solo per i registri.

Problemi correlati