Iniciar sesión dentro de las pruebas de pytest

90

Me gustaría poner algunas declaraciones de registro dentro de la función de prueba para examinar algunas variables de estado.

Tengo el siguiente fragmento de código:

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

Obtengo el siguiente resultado:

[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 

Tenga en cuenta que solo los mensajes de registro del '__name__ == __main__'bloque se transmiten a la consola.

¿Hay alguna forma de forzar la pytestemisión de registros a la consola desde los métodos de prueba también?

superselector
fuente
3
Puede echar un vistazo a esta respuesta , publicada por el creador de py.test. Sugiere un complemento de Pytest que proporciona un alto grado de versatilidad.
chb

Respuestas:

30

Funciona para mí, aquí está el resultado que obtengo: [recorte -> ejemplo era incorrecto]

Editar: Parece que tienes que pasar la -sopción a py.test para que no capture stdout. Aquí (py.test no instalado), fue suficiente para usar python pytest.py -s pyt.py.

Para su código, todo lo que necesita es pasar -sen argsa main:

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

Consulte la documentación de py.test sobre la captura de resultados .

TryPyPy
fuente
Lo siento. Pegué el código a toda prisa. Elimine la "aserción 0 == 1" de la función "test_one" para notar el "problema". Solo cuando hay alguna falla (que forcé al tener una afirmación falsa), py.test parece imprimir la información de registro.
superselector
No hay problema, descubrí cómo solucionarlo en la línea de comandos, buscando una forma programática.
TryPyPy
1
también puede redirigir la salida del registro a algún archivo en lugar del stderr implícito predeterminado.
hpk42
@superselector hpk42 es el tipo de py.test, escucha. IIUC, en tu código estaría logging.basicConfig(filename="somelog.txt", level=logging.DEBUG).
TryPyPy
115

Desde la versión 3.3, pytestadmite el registro en vivo, lo que significa que todos los registros emitidos en las pruebas se imprimirán en el terminal de inmediato. La función está documentada en la sección Registros en vivo . El registro en vivo está deshabilitado de forma predeterminada; para habilitarlo, configúrelo log_cli = 1en la pytest.iniconfiguración 1 . El registro en vivo admite la emisión a la terminal y al archivo; las opciones relevantes permiten la personalización de registros:

terminal:

  • log_cli_level
  • log_cli_format
  • log_cli_date_format

archivo:

  • log_file
  • log_file_level
  • log_file_format
  • log_file_date_format

Nota : la log_clibandera no se puede pasar desde la línea de comando y debe estar configurada pytest.ini. Todas las demás opciones pueden pasarse desde la línea de comandos o configurarse en el archivo de configuración. Como señaló Kévin Barré en este comentario , se pueden anular las opciones ini desde la línea de comandos a través de la -o/--overrideopción. Así que en lugar de declarar log_clien pytest.ini, sólo tiene que llamar:

$ pytest -o log_cli=true ...

Ejemplos

Archivo de prueba simple utilizado para demostrar:

# 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

Como puede ver, no se necesita configuración adicional; pytestconfigurará el registrador automáticamente, según las opciones especificadas pytest.inio pasadas desde la línea de comandos.

Registro en vivo a terminal, INFOnivel, salida elegante

Configuración 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

Ejecutando la prueba:

$ 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 =============================

Registro en vivo en terminal y archivo, solo mensaje y CRITICALnivel en terminal, salida elegante en pytest.logarchivo

Configuración 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

Prueba de funcionamiento:

$ 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 Aunque puede configurarlo pytesten setup.cfgla [tool:pytest]sección, no se sienta tentado a hacerlo cuando desee proporcionar un formato de registro en vivo personalizado. Otras herramientas de lectura setup.cfgpueden tratar cosas %(message)scomo una interpolación de cadenas y fallar. Úselo pytest.inipara evitar errores.

hoefling
fuente
17
Acerca de la nota que log_clidebe estar en pytest.ini , parece que puede usar la -oopción para anular el valor de la línea de comando. pytest -o log_cli=true --log-cli-level=DEBUGfunciona para mi.
Kévin Barré
@ KévinBarré muy buen comentario y una pista muy útil en general, ¡gracias! Actualización de la respuesta.
hoefling
Esta es definitivamente la respuesta correcta cuando se usa el registro. Aunque me gusta diferenciar los registros que están dentro de las pruebas y los registros que están dentro del sistema bajo prueba, que deben considerarse por separado.
CMCDragonkai
@CMCDragonkai desafortunadamente, pytestes algo limitado en ese asunto. Sin embargo, esto debería ser posible con una configuración de registro especial para las pruebas en su aplicación; desactive la propagación en sus registradores y agregue un "controlador de prueba" que registre un archivo específico. De esta manera, pytestsolo registra los registros que provienen de las pruebas, mientras que el controlador personalizado se encarga de los registros de SuT.
hoefling
1
@OfekAgmon si desea almacenar la pytestsalida, puede usar el --result-logargumento (aunque tenga en cuenta que está en desuso, aquí están las alternativas ). Sin pytestembargo, no puede almacenar la salida y la salida del registro en vivo en el mismo archivo.
hoefling