Registrazione nei test pytest


94

Vorrei inserire alcune dichiarazioni di registrazione all'interno della funzione di test per esaminare alcune variabili di stato.

Ho il seguente frammento di codice:

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

Ottengo il seguente output:

[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 

Si noti che solo i messaggi di registrazione dal '__name__ == __main__'blocco vengono trasmessi alla console.

C'è un modo per forzare pytestl'emissione della registrazione alla console anche dai metodi di test?


3
Potresti dare un'occhiata a questa risposta , pubblicata dal creatore di py.test. Suggerisce un plugin pytest che fornisce un alto grado di versatilità.
chb

Risposte:


30

Per me funziona, ecco l'output che ottengo: [snip -> example was wrong]

Modifica: sembra che tu debba passare l' -sopzione a py.test in modo che non catturi lo stdout. Qui (py.test non installato), è stato sufficiente utilizzare python pytest.py -s pyt.py.

Per il vostro codice, tutto ciò che serve è di passare -sin argsa main:

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

Vedere la documentazione di py.test sull'acquisizione dell'output .


Scusate. Ho incollato il codice in fretta. Rimuovere "assert 0 == 1" dalla funzione "test_one" per notare il "problema". Solo quando si verifica un errore (che ho forzato con una falsa asserzione), py.test sembra stampare le informazioni di registrazione.
superselettore

Nessun problema, ho scoperto come risolvere da riga di comando, cercando un modo programmatico.
TryPyPy

1
potresti anche reindirizzare l'output di registrazione su un file invece dello stderr implicito predefinito.
hpk42

@superselector hpk42 è il ragazzo di py.test, ascolta. IIUC, nel tuo codice sarebbe logging.basicConfig(filename="somelog.txt", level=logging.DEBUG).
TryPyPy

128

Dalla versione 3.3, pytest supporta la registrazione in tempo reale, il che significa che tutti i record di registro emessi nei test verranno stampati immediatamente sul terminale. La funzionalità è documentata nella sezione Live Logs . La registrazione in tempo reale è disabilitata per impostazione predefinita; per abilitarlo impostare log_cli = 1in pyproject.toml1 o pytest.ini2 . La registrazione live supporta l'emissione su terminale e file; le relative opzioni consentono la personalizzazione dei record:

terminale:

  • log_cli_level
  • log_cli_format
  • log_cli_date_format

file:

  • log_file
  • log_file_level
  • log_file_format
  • log_file_date_format

Nota : il log_cliflag non può essere passato dalla riga di comando e deve essere impostato in pytest.ini. Tutte le altre opzioni possono essere sia passate dalla riga di comando che impostate nel file di configurazione. Come sottolineato da Kévin Barré in questo commento , l'override delle opzioni ini dalla riga di comando può essere eseguito tramite l' -o/--overrideopzione. Quindi, invece di dichiarare log_cliin pytest.ini, puoi semplicemente chiamare:

$ pytest -o log_cli=true ...

Esempi

File di test semplice utilizzato per dimostrare:

# 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

Come puoi vedere, non è necessaria alcuna configurazione aggiuntiva; pytestimposterà il logger automaticamente, in base alle opzioni specificate pytest.inio passate dalla riga di comando.

Registrazione live su terminale, INFOlivello, output di fantasia

Configurazione in pyproject.toml:

[tool.pytest.ini_options]
log_cli = true
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"

La configurazione identica in legacy 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

Esecuzione del 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 =============================

Registrazione in tempo reale su terminale e file, solo messaggi e CRITICAL livello nel terminale, output di fantasia nel pytest.logfile

Configurazione in pyproject.toml:

[tool.pytest.ini_options]
log_cli = true
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"

La configurazione identica in legacy 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

Prova:

$ 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 pyproject.toml supportato dalla versione 6.0 ed è la migliore opzione IMO. Vedere PEP 518 per le specifiche.

2 Anche se è anche possibile configurare pytestin setup.cfgsotto la [tool:pytest]sezione, non essere tentati di farlo quando si desidera fornire formato di registrazione dal vivo personalizzato. Altri strumenti di lettura setup.cfgpotrebbero trattare cose come %(message)sl'interpolazione di stringhe e fallire. La scelta migliore è pyproject.tomlcomunque usare , ma se sei costretto a usare il formato ini-style legacy, attenersi a pytest.iniper evitare errori.


21
Riguardo alla nota che log_clideve essere in pytest.ini , sembra che tu possa usare l' -oopzione per sovrascrivere il valore dalla riga di comando. pytest -o log_cli=true --log-cli-level=DEBUGper me va bene.
Kévin Barré

@ KévinBarré commento molto bello e un suggerimento molto utile in generale, grazie! Aggiornata la risposta.
Hoefling

Questa è sicuramente la risposta corretta quando si utilizza la registrazione. Anche se mi piace differenziare i log che si trovano all'interno dei test e i log che si trovano all'interno del sistema sotto test che dovrebbero essere considerati separatamente.
CMCDragonkai

@CMCDragonkai purtroppo pytestè un po 'limitato in questa materia. Tuttavia, ciò dovrebbe essere fattibile con una configurazione di registrazione speciale per i test nella tua app; disattiva la propagazione nei tuoi logger e aggiungi un "gestore di test" che accede a un file specificato. In questo modo, pytestregistra solo i record provenienti dai test, mentre il gestore personalizzato si occupa dei log di SuT.
Hoefling

1
@OfekAgmon se vuoi memorizzare l' pytestoutput, puoi usare l' --result-logargomento (anche se nota che è deprecato, ecco le alternative ). pytestTuttavia, non è possibile memorizzare l' output e l'output della registrazione live nello stesso file.
hoefling
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.