[python] Python Django에서 단위 테스트를 실행하는 동안 로깅을 비활성화하려면 어떻게합니까?

Django 응용 프로그램을 테스트하기 위해 간단한 단위 테스트 기반 테스트 러너를 사용하고 있습니다.

내 응용 프로그램 자체는 settings.py에서 기본 로거를 사용하도록 구성되었습니다.

logging.basicConfig(level=logging.DEBUG)

그리고 내 응용 프로그램 코드에서 다음을 사용합니다.

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

그러나 unittests를 실행할 때 테스트 결과 출력을 어지럽히 지 않도록 로깅을 비활성화하고 싶습니다. 테스트를 실행할 때 응용 프로그램 특정 로거가 콘솔에 내용을 쓰지 않도록 전역 방식으로 로깅을 해제하는 간단한 방법이 있습니까?



답변

logging.disable(logging.CRITICAL)

수준이보다 낮거나 같은 모든 로깅 호출을 비활성화합니다 CRITICAL. 로 로깅을 다시 활성화 할 수 있습니다

logging.disable(logging.NOTSET)


답변

장고에 있기 때문에 설정에 다음 줄을 추가 할 수 있습니다.

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', ...]


답변

테스트를 실행할 때 응용 프로그램 특정 로거가 콘솔에 내용을 쓰지 않도록 전역 방식으로 로깅을 해제하는 간단한 방법이 있습니까?

다른 답변은 로깅 인프라를 전역 적으로 설정하여 아무것도 무시하도록 “콘솔에 내용을 쓰는”것을 방지합니다. 이것은 효과가 있지만 너무 무딘 접근 방식을 발견했습니다. 내 접근 방식은 구성 변경을 수행하여 콘솔에서 로그가 유출되는 것을 방지하는 데 필요한 것만 수행합니다. 그래서 내 사용자 지정 로깅 필터 를 추가합니다 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

그리고 필터를 사용 하도록 장고 로깅구성합니다 .

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)는 테스트 중에 로그를 캡처하고 테스트 실패와 함께 로그의 관련 부분을 출력합니다. 테스트가 실패한 이유를 알아내는 데 유용합니다. 로깅이 완전히 해제 된 경우 캡처 할 수있는 것이 없습니다.


답변

Hassek의 맞춤형 테스트 러너 아이디어가 마음에 듭니다. 그것은 그 관찰되지해야 DjangoTestSuiteRunner더 이상 장고 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)


답변

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)


답변

특정 메소드에서 로깅을 비활성화하기 위해 간단한 메소드 데코레이터를 사용하고 있습니다.

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


답변

메소드를 사용하여 테스트에서 로깅을 일시 중지하는 예쁘고 깨끗한 방법이 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로깅 출력을 생성하지 않습니다.