Journalisation dans les tests pytest

90

Je voudrais mettre quelques instructions de journalisation dans la fonction de test pour examiner certaines variables d'état.

J'ai l'extrait de code suivant:

import pytest,os
import logging

logging.basicConfig(level=logging.DEBUG)
mylogger = logging.getLogger()

#############################################################################

def setup_module(module):
    ''' Setup for the entire module '''
    mylogger.info('Inside Setup')
    # Do the actual setup stuff here
    pass

def setup_function(func):
    ''' Setup for test functions '''
    if func == test_one:
        mylogger.info(' Hurray !!')

def test_one():
    ''' Test One '''
    mylogger.info('Inside Test 1')
    #assert 0 == 1
    pass

def test_two():
    ''' Test Two '''
    mylogger.info('Inside Test 2')
    pass

if __name__ == '__main__':
    mylogger.info(' About to start the tests ')
    pytest.main(args=[os.path.abspath(__file__)])
    mylogger.info(' Done executing the tests ')

J'obtiens la sortie suivante:

[bmaryada-mbp:/Users/bmaryada/dev/platform/main/proto/tests/tpch $]python minitest.py
INFO:root: About to start the tests 
======================================================== test session starts =========================================================
platform darwin -- Python 2.6.2 -- pytest-2.0.0
collected 2 items 

minitest.py ..

====================================================== 2 passed in 0.01 seconds ======================================================
INFO:root: Done executing the tests 

Notez que seuls les messages de journalisation du '__name__ == __main__'bloc sont transmis à la console.

Existe-t-il également un moyen de forcer l'émission pytestde journaux vers la console à partir de méthodes de test?

supersélecteur
la source
3
Vous pouvez jeter un œil à cette réponse , publiée par le créateur de py.test. Il suggère un plugin pytest qui offre une grande polyvalence.
chb

Réponses:

30

Fonctionne pour moi, voici le résultat que j'obtiens: [snip -> example was incorrect]

Edit: Il semble que vous deviez passer l' -soption à py.test pour qu'il ne capture pas stdout. Ici (py.test non installé), il suffisait de l'utiliser python pytest.py -s pyt.py.

Pour votre code, il suffit de passer -sdans argsà main:

 pytest.main(args=['-s', os.path.abspath(__file__)])

Consultez la documentation py.test sur la capture de la sortie .

TryPyPy
la source
Pardon. J'ai collé le code à la hâte. Veuillez supprimer «assert 0 == 1» de la fonction «test_one» pour remarquer le «problème». Ce n'est qu'en cas d'échec (que j'ai forcé en ayant une fausse assertion), py.test semble afficher les informations de journalisation.
superselector
Pas de problème, j'ai trouvé comment réparer sur la ligne de commande, à la recherche d'une méthode programmatique.
TryPyPy
1
vous pouvez également rediriger la sortie de journalisation vers un fichier au lieu du stderr implicite par défaut.
hpk42
@superselector hpk42 est le gars py.test, écoutez. IIUC, dans votre code ce serait logging.basicConfig(filename="somelog.txt", level=logging.DEBUG).
TryPyPy
115

Depuis la version 3.3, pytestprend en charge la journalisation en direct, ce qui signifie que tous les enregistrements de journal émis lors des tests seront immédiatement imprimés sur le terminal. La fonctionnalité est documentée dans la section Live Logs . La journalisation en direct est désactivée par défaut; pour l'activer, définissez-le log_cli = 1dans la pytest.iniconfiguration 1 . La journalisation en direct prend en charge l'émission vers le terminal et le fichier; les options appropriées permettent la personnalisation des enregistrements:

Terminal:

  • log_cli_level
  • log_cli_format
  • log_cli_date_format

fichier:

  • log_file
  • log_file_level
  • log_file_format
  • log_file_date_format

Remarque : l' log_cliindicateur ne peut pas être passé depuis la ligne de commande et doit être défini dans pytest.ini. Toutes les autres options peuvent être à la fois passées à partir de la ligne de commande ou définies dans le fichier de configuration. Comme l'a souligné Kévin Barré dans ce commentaire , le remplacement des options ini à partir de la ligne de commande peut se faire via l' -o/--overrideoption. Ainsi, au lieu de déclarer log_cliin pytest.ini, vous pouvez simplement appeler:

$ pytest -o log_cli=true ...

Exemples

Fichier de test simple utilisé pour démontrer:

# test_spam.py

import logging

LOGGER = logging.getLogger(__name__)


def test_eggs():
    LOGGER.info('eggs info')
    LOGGER.warning('eggs warning')
    LOGGER.error('eggs error')
    LOGGER.critical('eggs critical')
    assert True

Comme vous pouvez le voir, aucune configuration supplémentaire n'est nécessaire; pytestconfigurera l'enregistreur automatiquement, en fonction des options spécifiées pytest.iniou transmises depuis la ligne de commande.

Journalisation en direct vers le terminal, le INFOniveau, la sortie fantaisie

Configuration en pytest.ini:

[pytest]
log_cli = 1
log_cli_level = INFO
log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
log_cli_date_format=%Y-%m-%d %H:%M:%S

Exécution du test:

$ pytest test_spam.py
=============================== test session starts ================================
platform darwin -- Python 3.6.4, pytest-3.7.0, py-1.5.3, pluggy-0.7.1 -- /Users/hoefling/.virtualenvs/stackoverflow/bin/python3.6
cachedir: .pytest_cache
rootdir: /Users/hoefling/projects/private/stackoverflow/so-4673373, inifile: pytest.ini
collected 1 item

test_spam.py::test_eggs
---------------------------------- live log call -----------------------------------
2018-08-01 14:33:20 [    INFO] eggs info (test_spam.py:7)
2018-08-01 14:33:20 [ WARNING] eggs warning (test_spam.py:8)
2018-08-01 14:33:20 [   ERROR] eggs error (test_spam.py:9)
2018-08-01 14:33:20 [CRITICAL] eggs critical (test_spam.py:10)
PASSED                                                                        [100%]

============================= 1 passed in 0.01 seconds =============================

Connexion en direct au terminal et au fichier, uniquement message et CRITICALniveau dans le terminal, sortie fantaisie dans le pytest.logfichier

Configuration en pytest.ini:

[pytest]
log_cli = 1
log_cli_level = CRITICAL
log_cli_format = %(message)s

log_file = pytest.log
log_file_level = DEBUG
log_file_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
log_file_date_format=%Y-%m-%d %H:%M:%S

Essai:

$ pytest test_spam.py
=============================== test session starts ================================
platform darwin -- Python 3.6.4, pytest-3.7.0, py-1.5.3, pluggy-0.7.1 -- /Users/hoefling/.virtualenvs/stackoverflow/bin/python3.6
cachedir: .pytest_cache
rootdir: /Users/hoefling/projects/private/stackoverflow/so-4673373, inifile: pytest.ini
collected 1 item

test_spam.py::test_eggs
---------------------------------- live log call -----------------------------------
eggs critical
PASSED                                                                        [100%]

============================= 1 passed in 0.01 seconds =============================

$ cat pytest.log
2018-08-01 14:38:09 [    INFO] eggs info (test_spam.py:7)
2018-08-01 14:38:09 [ WARNING] eggs warning (test_spam.py:8)
2018-08-01 14:38:09 [   ERROR] eggs error (test_spam.py:9)
2018-08-01 14:38:09 [CRITICAL] eggs critical (test_spam.py:10)

1 Bien que vous puissiez configurer pytestdans setup.cfgsous la [tool:pytest]section, ne soyez pas tenté de le faire lorsque vous souhaitez fournir un format de journalisation en direct personnalisé. D'autres outils de lecture setup.cfgpeuvent traiter des choses comme %(message)sune interpolation de chaîne et échouer. Utilisez pytest.inipour éviter les erreurs.

hoefling
la source
17
À propos de la note qui log_clidoit être dans pytest.ini , il semble que vous puissiez utiliser l' -ooption pour remplacer la valeur de la ligne de commande. pytest -o log_cli=true --log-cli-level=DEBUGtravaille pour moi.
Kévin Barré
@ KévinBarré très gentil commentaire et un indice très utile en général, merci! Mise à jour de la réponse.
hoefling le
C'est certainement la bonne réponse lors de l'utilisation de la journalisation. Bien que j'aime différencier les journaux qui se trouvent à l'intérieur des tests, et les journaux qui se trouvent à l'intérieur du système testé, qui doivent être considérés séparément.
CMCDragonkai
@CMCDragonkai, malheureusement, pytestest quelque peu limité à cet égard. Cependant, cela devrait être faisable avec une configuration de journalisation spéciale pour les tests dans votre application; désactivez la propagation dans vos enregistreurs et ajoutez un "gestionnaire de test" qui se connecte à un fichier spécifié. De cette façon, pytestne journalise que les enregistrements provenant de tests, tandis que le gestionnaire personnalisé s'occupe des journaux SuT.
hoefling le
1
@OfekAgmon si vous souhaitez stocker la pytestsortie, vous pouvez utiliser l' --result-logargument (bien que notez qu'il soit obsolète, voici les alternatives ). pytestCependant, vous ne pouvez pas stocker la sortie et la sortie de journalisation en direct dans le même fichier.
hoefling