unittest - ¿Cómo puedo desactivar el registro mientras realizo pruebas unitarias en Python Django?
python pruebas (8)
Estoy usando un corredor de prueba basado en una prueba simple para probar mi aplicación Django.
Mi aplicación está configurada para usar un registrador básico en settings.py usando:
logging.basicConfig(level=logging.DEBUG)
Y en mi código de aplicación usando:
logger = logging.getLogger(__name__)
logger.setLevel(getattr(settings, ''LOG_LEVEL'', logging.DEBUG))
Sin embargo, cuando ejecuto tests unitarios, me gustaría deshabilitar el registro para que no atente la salida de mi resultado de prueba. ¿Existe una manera simple de desactivar el registro de manera global, de modo que los registradores específicos de la aplicación no escriban cosas en la consola cuando ejecuto las pruebas?
¿Existe una manera simple de desactivar el registro de manera global, de modo que los registradores específicos de la aplicación no escriban cosas en la consola cuando ejecuto las pruebas?
Las otras respuestas evitan "escribir cosas en la consola" configurando globalmente la infraestructura de registro para ignorar cualquier cosa. Esto funciona, pero me parece demasiado directo. Mi enfoque es realizar un cambio de configuración que solo hace lo necesario para evitar que los registros se salgan en la consola. Así que agrego un filtro de registro personalizado a mi settings.py
:
from logging import Filter
class NotInTestingFilter(Filter):
def filter(self, record):
# Although I normally just put this class in the settings.py
# file, I have my reasons to load settings here. In many
# cases, you could skip the import and just read the setting
# from the local symbol space.
from django.conf import settings
# TESTING_MODE is some settings variable that tells my code
# whether the code is running in a testing environment or
# not. Any test runner I use will load the Django code in a
# way that makes it True.
return not settings.TESTING_MODE
Y configuro el registro de Django para usar el filtro:
LOGGING = {
''version'': 1,
''disable_existing_loggers'': False,
''filters'': {
''testing'': {
''()'': NotInTestingFilter
}
},
''formatters'': {
''verbose'': {
''format'': (''%(levelname)s %(asctime)s %(module)s ''
''%(process)d %(thread)d %(message)s'')
},
},
''handlers'': {
''console'': {
''level'': ''DEBUG'',
''class'': ''logging.StreamHandler'',
''filters'': [''testing''],
''formatter'': ''verbose''
},
},
''loggers'': {
''foo'': {
''handlers'': [''console''],
''level'': ''DEBUG'',
''propagate'': True,
},
}
}
Resultado final: cuando estoy probando, nada va a la consola, pero todo lo demás permanece igual.
¿Por qué hacer esto?
Diseño un código que contiene instrucciones de registro que se activan solo en circunstancias específicas y que deberían generar los datos exactos que necesito para el diagnóstico si las cosas van mal. Por lo tanto, pruebo que hacen lo que se supone que deben hacer y, por lo tanto, no es viable deshabilitar completamente el registro. No quiero encontrar una vez que el software está en producción, que lo que pensé que sería registrado no está registrado.
Además, algunos corredores de prueba (Nose, por ejemplo) capturarán los registros durante la prueba y generarán la parte relevante del registro junto con una falla de prueba. Es útil para descubrir por qué falló una prueba. Si el registro está completamente desactivado, no hay nada que pueda capturarse.
A veces quieres los registros y otras no. Tengo este código en mi settings.py
import sys
if ''--no-logs'' in sys.argv:
print(''> Disabling logging levels of CRITICAL and below.'')
sys.argv.remove(''--no-logs'')
logging.disable(logging.CRITICAL)
Entonces, si ejecuta su prueba con las opciones --no-logs
, obtendrá solo los registros critical
:
$ python ./manage.py tests --no-logs
> Disabling logging levels of CRITICAL and below.
Es muy útil si desea acelerar las pruebas en su flujo de integración continua.
Como estás en Django, puedes agregar estas líneas a tu settings.py:
import sys
import logging
if len(sys.argv) > 1 and sys.argv[1] == ''test'':
logging.disable(logging.CRITICAL)
De esta forma, no tiene que agregar esa línea en cada setUp () en sus pruebas. :)
También podría hacer un par de cambios útiles para sus necesidades de prueba de esta manera.
Hay otra manera "más agradable" o "más limpia" de agregar detalles a sus pruebas y eso es hacer su propio corredor de prueba.
Solo crea una clase como esta:
import logging
from django.test.simple import DjangoTestSuiteRunner
from django.conf import settings
class MyOwnTestRunner(DjangoTestSuiteRunner):
def run_tests(self, test_labels, extra_tests=None, **kwargs):
# Don''t show logging messages while testing
logging.disable(logging.CRITICAL)
return super(MyOwnTestRunner, self).run_tests(test_labels, extra_tests, **kwargs)
Y ahora agregue a su archivo settings.py:
TEST_RUNNER = "PATH.TO.PYFILE.MyOwnTestRunner"
#(for example, ''utils.mytest_runner.MyOwnTestRunner'')
Esto le permite hacer una modificación realmente práctica que no tiene el otro enfoque, que es hacer que Django simplemente pruebe las aplicaciones que desea. Puedes hacer eso cambiando las etiquetas de prueba agregando esta línea al corredor de prueba:
if not test_labels:
test_labels = [''my_app1'', ''my_app2'', ...]
Descubrí que para las pruebas dentro de un test o un marco similar, la forma más efectiva de desactivar de manera segura el registro no deseado en pruebas unitarias es habilitar / deshabilitar en los métodos setUp
/ tearDown
de un caso de prueba en particular. Esto permite que un objetivo específicamente donde los registros deben ser deshabilitados. También puede hacer esto explícitamente en el registrador de la clase que está probando.
import unittest
import logging
class TestMyUnitTest(unittest.TestCase):
def setUp(self):
logging.disable(logging.CRITICAL)
def tearDown(self):
logging.disable(logging.NOTSET)
En mi caso, tengo una configuración de archivo de settings/test.py
creada específicamente para fines de prueba, así es como se ve:
from .base import *
DATABASES = {
''default'': {
''ENGINE'': ''django.db.backends.sqlite3'',
''NAME'': ''test_db''
}
}
PASSWORD_HASHERS = (
''django.contrib.auth.hashers.MD5PasswordHasher'',
)
LOGGING = {}
Puse una variable de entorno DJANGO_SETTINGS_MODULE=settings.test
en /etc/environment
.
Existe algún método bonito y limpio para suspender el registro en pruebas con el método unittest.mock.patch
.
foo.py :
import logging
logger = logging.getLogger(__name__)
def bar():
logger.error(''There is some error output here!'')
return True
tests.py :
from unittest import mock, TestCase
from foo import bar
class FooBarTestCase(TestCase):
@mock.patch(''foo.logger'', mock.Mock())
def test_bar(self):
self.assertTrue(bar())
Y las python3 -m unittest tests
no producirán salida de registro.
Me gusta la idea del corredor de pruebas personalizado de Hassek. Cabe señalar que DjangoTestSuiteRunner
ya no es el corredor de prueba predeterminado en Django 1.6+, sino que ha sido reemplazado por el DiscoverRunner
. Para el comportamiento predeterminado, el corredor de prueba debería ser más como:
import logging
from django.test.runner import DiscoverRunner
class NoLoggingTestRunner(DiscoverRunner):
def run_tests(self, test_labels, extra_tests=None, **kwargs):
# disable logging below CRITICAL while testing
logging.disable(logging.CRITICAL)
return super(NoLoggingTestRunner, self).run_tests(test_labels, extra_tests, **kwargs)
logging.disable(logging.CRITICAL)
deshabilitará todas las llamadas de registro con niveles menos severos o iguales a CRITICAL
. El registro se puede volver a habilitar con
logging.disable(logging.NOTSET)