Как отключить ведение журнала при запуске модульных тестов в Python Django? - PullRequest
149 голосов
/ 10 марта 2011

Я использую простой тестовый модуль на основе модульных тестов для тестирования моего приложения Django.

Мое приложение само настроено на использование базового регистратора в settings.py с помощью:

logging.basicConfig(level=logging.DEBUG)

И в моем коде приложения, используя:

logger = logging.getLogger(__name__)
logger.setLevel(getattr(settings, 'LOG_LEVEL', logging.DEBUG))

Однако при запуске юнит-тестов я бы хотел отключить ведение журнала, чтобы он не загромождал вывод результатов моего теста. Есть ли простой способ отключить ведение журналов глобальным способом, чтобы при запуске тестов определенные логгеры приложений не выводили данные на консоль?

Ответы [ 11 ]

223 голосов
/ 10 марта 2011
logging.disable(logging.CRITICAL)

отключит запись всех вызовов с уровнями менее серьезными, чем CRITICAL Ведение журнала можно включить с помощью

logging.disable(logging.NOTSET)
42 голосов
/ 12 октября 2011

Так как вы находитесь в Django, вы можете добавить эти строки в свои настройки.py:

import sys
import logging

if len(sys.argv) > 1 and sys.argv[1] == 'test':
    logging.disable(logging.CRITICAL)

Таким образом, вам не нужно добавлять эту строку в каждый setUp () в ваших тестах. :)

Таким образом, вы также можете внести несколько удобных изменений в свои тестовые потребности.

Существует еще один "более приятный" или "более чистый" способ добавить конкретику в ваши тесты, и это ваш собственный организатор.

Просто создайте класс, подобный этому:

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)

А теперь добавьте в ваш файл settings.py:

TEST_RUNNER = "PATH.TO.PYFILE.MyOwnTestRunner"
#(for example, 'utils.mytest_runner.MyOwnTestRunner')

Это позволяет вам сделать одну действительно удобную модификацию, которую не делает другой подход, а именно заставить Django просто протестировать нужные приложения. Вы можете сделать это, изменив test_labels, добавив следующую строку в тестовый прогон:

if not test_labels:
    test_labels = ['my_app1', 'my_app2', ...]
21 голосов
/ 18 сентября 2015

Есть ли простой способ отключить ведение журнала глобальным способом, чтобы при запуске тестов определенные логгеры приложений не выводили данные на консоль?

Другие ответы предотвращают «запись материала на консоль», глобально настраивая инфраструктуру ведения журнала на игнорирование чего-либо. Это работает, но я считаю это слишком грубым подходом. Мой подход заключается в том, чтобы выполнить изменение конфигурации, которое делает только то, что необходимо для предотвращения выхода журналов на консоль. Поэтому я добавляю пользовательский фильтр регистрации к своему 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

И я настраиваю протоколирование Django для использования фильтра:

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,
        },
    }
}

Конечный результат: когда я тестирую, к консоли ничего не идет, но все остальное остается прежним.

Зачем это делать?

Я разрабатываю код, который содержит инструкции по протоколированию, которые срабатывают только при определенных обстоятельствах и которые должны выводить точные данные, которые мне нужны для диагностики, если что-то пойдет не так. Поэтому я проверяю , что они делают то, что должны, и, таким образом, полностью отключают ведение журнала, для меня нецелесообразно. Я не хочу найти, как только программное обеспечение будет запущено в производство, что то, что я думал будет зарегистрировано, не будет зарегистрировано.

Более того, некоторые тестеры (например, Nose) будут захватывать журналы во время тестирования и выводить соответствующую часть журнала вместе с ошибкой теста. Полезно выяснить, почему тест не удался. Если ведение журнала полностью отключено, то ничего не может быть захвачено.

20 голосов
/ 29 июля 2014

Мне нравится идея Хасека по индивидуальным тестам.Следует отметить, что DjangoTestSuiteRunner больше не является стандартным средством выполнения тестов в Django 1.6+, его заменили на DiscoverRunner.Для поведения по умолчанию бегун теста должен быть больше похож на:

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)
4 голосов
/ 12 апреля 2018

Я обнаружил, что для тестов в unittest или аналогичной среде наиболее эффективным способом безопасного отключения нежелательной регистрации в модульных тестах является включение / отключение в setUp / tearDown методов конкретного теста.дело.Это позволяет одной цели, где журналы должны быть отключены.Вы также можете сделать это явно на регистраторе класса, который вы тестируете.

import unittest
import logging

class TestMyUnitTest(unittest.TestCase):
    def setUp(self):
        logging.disable(logging.CRITICAL)

    def tearDown(self):
        logging.disable(logging.NOTSET)
3 голосов
/ 09 января 2018

Существует довольно приятный и чистый способ приостановить регистрацию в тестах с помощью метода 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())

И python3 -m unittest tests не будет выводить данные регистрации.

2 голосов
/ 04 апреля 2019

Я использую простой метод декоратора, чтобы отключить ведение журнала только в определенном методе тестирования.

def disable_logging(f):

    def wrapper(*args):
        logging.disable(logging.CRITICAL)
        result = f(*args)
        logging.disable(logging.NOTSET)

        return result

    return wrapper

И затем я использую его, как в следующем примере:

class ScenarioTestCase(TestCase):

    @disable_logging
    test_scenario(self):
        pass
1 голос
/ 04 февраля 2019

Если вы не хотите, чтобы он несколько раз включал / выключал его в setUp () и tearDown () для unittest (не вижу причины для этого), вы можете просто сделать это один раз для класса:

    import unittest
    import logging

    class TestMyUnitTest(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            logging.disable(logging.CRITICAL)
        @classmethod
        def tearDownClass(cls):
            logging.disable(logging.NOTSET)
1 голос
/ 13 июня 2017

Иногда вы хотите журналы, а иногда нет.У меня есть этот код в моем 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)

Так что если вы запустите свой тест с опциями --no-logs, вы получите только critical журналы:

$ python ./manage.py tests --no-logs
> Disabling logging levels of CRITICAL and below.

Это очень полезно, если вы хотите ускорить тестирование в процессе непрерывной интеграции.

0 голосов
/ 27 марта 2019

Если у вас есть разные модули инициализации для тестирования, разработки и производства, вы можете отключить что-либо или перенаправить его в инициализаторе.У меня есть local.py, test.py и production.py, которые все наследуются от common.y

common.py выполняет все основные настройки, включая этот фрагмент:

LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
    'django.server': {
        '()': 'django.utils.log.ServerFormatter',
        'format': '[%(server_time)s] %(message)s',
    },
    'verbose': {
        'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
    },
    'simple': {
        'format': '%(levelname)s %(message)s'
    },
},
'filters': {
    'require_debug_true': {
        '()': 'django.utils.log.RequireDebugTrue',
    },
},
'handlers': {
    'django.server': {
        'level': 'INFO',
        'class': 'logging.StreamHandler',
        'formatter': 'django.server',
    },
    'console': {
        'level': 'DEBUG',
        'class': 'logging.StreamHandler',
        'formatter': 'simple'
    },
    'mail_admins': {
        'level': 'ERROR',
        'class': 'django.utils.log.AdminEmailHandler'
    }
},
'loggers': {
    'django': {
        'handlers': ['console'],
        'level': 'INFO',
        'propagate': True,
    },
    'celery.tasks': {
        'handlers': ['console'],
        'level': 'DEBUG',
        'propagate': True,
    },
    'django.server': {
        'handlers': ['django.server'],
        'level': 'INFO',
        'propagate': False,
    },
}

Затем в тесте.py У меня есть это:

console_logger = Common.LOGGING.get('handlers').get('console')
console_logger['class'] = 'logging.FileHandler
console_logger['filename'] = './unitest.log

Это заменяет обработчик консоли FileHandler и означает, что по-прежнему получать журналы, но мне не нужно трогать базу производственного кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...