Récemment, j'écris une extension de journalisation python et je souhaite ajouter des tests pour mon extension afin de vérifier si mon extension fonctionne comme prévu.
Cependant, je ne sais pas comment capturer le journal complet et le comparer avec mon résultat excepté dans unittest / pytest.
exemple simplifié :
err = ''
Voici mes tests
Tentative 1: unittest
err = 'app-DEBUG-hello'
def test_logger(capsys): app_logger.debug('hello') out, err = capsys.readouterr() assert err assert err == 'app-DEBUG-hello'
caplog.text = 'test_logger.py 6 DEBUG hello'
Tentative 2: pytest caplog
caplog.text = 'app-DEBUG-hello'
from app import app_logger import pytest def test_logger(caplog): app_logger.debug('hello') assert caplog.text == 'app-DEBUG-hello'
cm.output = ['DEBUG:app:hello']
Tentative 3: pytest capsys
depuis l'application import app_logger import pytest
cm.output = 'app-DEBUG-hello'
from app import app_logger import unittest class TestApp(unittest.TestCase): def test_logger(self): with self.assertLogs('', 'DEBUG') as cm: app_logger.debug('hello') # or some other way to capture the log output. self.assertEqual('app-DEBUG-hello', cm.output)
# app.py import logging def create_logger(): formatter = logging.Formatter(fmt='%(name)s-%(levelname)s-%(message)s') hdlr = logging.StreamHandler() hdlr.setFormatter(formatter) logger = logging.getLogger(__name__) logger.setLevel('DEBUG') logger.addHandler(hdlr) return logger app_logger = create_logger()
Étant donné qu'il y aura de nombreux tests avec différents formats, je ne veux pas vérifier le format du journal manuellement. Je n'ai aucune idée de comment obtenir le journal complet comme je le vois sur la console et le comparer avec mon attendu dans les cas de test. En espérant votre aide, merci.
3 Réponses :
Après avoir lu le code source de la bibliothèque unittest
, j'ai élaboré le contournement suivant. Notez qu'il fonctionne en modifiant un membre protégé d'un module importé, il peut donc être interrompu dans les versions futures.
from unittest.case import _AssertLogsContext _AssertLogsContext.LOGGING_FORMAT = 'same format as your logger'
Après ces commandes, le contexte de journalisation est ouvert par self.assertLogs code > utilisera le format ci-dessus. Je ne sais vraiment pas pourquoi cette valeur est restée codée en dur et non configurable.
Je n'ai pas trouvé d'option pour lire le format d'un enregistreur, mais si vous utilisez logging.config. dictConfig
vous pouvez utiliser une valeur du même dictionnaire.
Je sais que c'est vieux, mais poster ici car il s'est retrouvé dans Google pour moi ...
Il faudrait probablement un nettoyage, mais c'est la première chose qui s'est rapprochée pour moi, alors j'ai pensé que ce serait bien à partager.
Voici un mixin de cas de test que j'ai mis en place qui me permet de vérifier qu'un gestionnaire particulier est formaté comme prévu en copiant le formateur:
class SimpleLogTests(SetupLoggingMixin, SimpleTestCase): def test_logged_time(self): msg = 'foo' self.root_logger.error(msg) self.assertEqual(self.stream.getvalue(), 'my-expected-message-formatted-as-expected')
Et voici un exemple de son utilisation:
import io import logging from django.conf import settings from django.test import SimpleTestCase from django.utils.log import DEFAULT_LOGGING class SetupLoggingMixin: def setUp(self): super().setUp() logging.config.dictConfig(settings.LOGGING) self.stream = io.StringIO() self.root_logger = logging.getLogger("") self.root_hdlr = logging.StreamHandler(self.stream) console_handler = None for handler in self.root_logger.handlers: if handler.name == 'console': console_handler = handler break if console_handler is None: raise RuntimeError('could not find console handler') formatter = console_handler.formatter self.root_formatter = formatter self.root_hdlr.setFormatter(self.root_formatter) self.root_logger.addHandler(self.root_hdlr) def tearDown(self): super().tearDown() self.stream.close() logging.config.dictConfig(DEFAULT_LOGGING)
Je sais que cela ne répond pas complètement à la question du PO, mais je suis tombé sur ce message en cherchant un moyen efficace de capturer les messages enregistrés.
En prenant ce que @ user319862 a fait, je l'ai nettoyé et simplifié.
import unittest import logging from io import StringIO class SetupLogging(unittest.TestCase): def setUp(self): super().setUp() self.stream = StringIO() self.root_logger = logging.getLogger("") self.root_hdlr = logging.StreamHandler(self.stream) self.root_logger.addHandler(self.root_hdlr) def tearDown(self): super().tearDown() self.stream.close() def test_log_output(self): """ Does the logger produce the correct output? """ msg = 'foo' self.root_logger.error(msg) self.assertEqual(self.stream.getvalue(), 'foo\n') if __name__ == '__main__': unittest.main()
C'est parce que votre gestionnaire sera ignoré lorsque les enregistrements de journal émis sont capturés, à la fois par
unittest
etpytest
. En ce qui concerne l'application du formatage personnalisé, le support est soit complètement absent (unittest
ne prend pas du tout en charge les formateurs personnalisés) soit assez limité (avecpytest
, vous pouvez au moins vous passer chaîne de format personnalisé via la ligne de commande:pytest --log-format = "% (name) s -% (levelname) s -% (message) s"
, mais les classes de formateur personnalisées seront également ignorées ).Cependant, tester le format des enregistrements collectés tout au long du programme rapporte rarement de toute façon; divisez vos tests en: ceux qui vérifient si les enregistrements du journal sont correctement émis (par exemple, vous ne manquez pas un enregistrement lors de l'exécution d'une fonction) et ceux qui valident vos gestionnaires et formateurs personnalisés (créez un enregistrement, appelez le gestionnaire
. émettre (record)
/formatter.format (record)
explicilty et vérifier s'ils ont bien fait leur travail).@hoefling compris. Merci pour votre réponse rapide :) Ce que mon extension fait principalement est d'ajouter un champ supplémentaire pour le connecter et de le formater avec le format spécifié. Il semble qu'il soit un peu difficile de vérifier son format, mais vous me fournissez un autre moyen de le réaliser. Je vais essayer. Je vous remercie.