web-dev-qa-db-ja.com

Python Django?で単体テストを実行中にロギングを無効にするにはどうすればよいですか?

Djangoアプリケーションをテストするために、単純な単体テストベースのテストランナーを使用しています。

私のアプリケーション自体は、次を使用してsettings.pyで基本的なロガーを使用するように構成されています。

logging.basicConfig(level=logging.DEBUG)

そして、私のアプリケーションコードでは次を使用します:

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

ただし、unittestsを実行するときは、テスト結果の出力が乱雑にならないようにログを無効にします。テストを実行するときにアプリケーション固有のロガーがコンソールにデータを書き込まないように、グローバルな方法でロギングをオフにする簡単な方法はありますか?

154
shreddd
logging.disable(logging.CRITICAL)

CRITICAL以下のレベルのすべてのロギングコールを無効にします。ロギングを再度有効にできます

logging.disable(logging.NOTSET)
227
unutbu

Djangoにいるので、次の行をsettings.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')
_

これにより、他のアプローチでは得られない非常に便利な変更を1つ行うことができます。つまり、Djangoは、必要なアプリケーションをテストするだけです。_test_labels_この行をテストランナーに追加します。

_if not test_labels:
    test_labels = ['my_app1', 'my_app2', ...]
_
42
Hassek

テストを実行するときにアプリケーション固有のロガーがコンソールにデータを書き込まないように、グローバルな方法でロギングをオフにする簡単な方法はありますか?

他の答えは、ログインフラストラクチャをグローバルに設定して何も無視しないようにすることで、「コンソールへのデータの書き込み」を防ぎます。これは機能しますが、私はあまりにもアプローチを鈍くしています。私のアプローチは、ログがコンソールに表示されないようにするために必要なことだけを行う構成変更を実行することです。そこで、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 を設定してフィルターを使用します:

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

最終結果:私がテストしているとき、コンソールには何も行きませんが、他のすべては同じままです。

なぜこれを行うのですか?

特定の状況でのみトリガーされるロギング命令を含むコードを設計し、問題が発生した場合に診断に必要な正確なデータを出力する必要があります。そのため、私はtestを実行して、想定されていることを実行するため、ロギングを完全に無効にすることは実行できません。ソフトウェアが実稼働状態になったら、私thoughtが記録されるものが記録されないことを知りたくありません。

さらに、一部のテストランナー(たとえば、Nose)は、テスト中にログをキャプチャし、テストの失敗と共にログの関連部分を出力します。テストが失敗した理由を理解するのに役立ちます。ロギングが完全にオフになっている場合、キャプチャできるものはありません。

21
Louis

Hassekのカスタムテストランナーのアイデアが気に入っています。 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)
20
alukach

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)
4
mcguip

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はログ出力を生成しません。

3
valex

簡単なメソッドデコレータを使用して、特定のテストメソッドでのみログを無効にします。

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
2
Eduard Mukans

UnittestのsetUp()およびtearDown()で繰り返しオン/オフにしたくない場合(その理由はわかりません)、クラスごとに1回だけ実行できます。

    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
the pillow

ログが必要な場合とそうでない場合があります。 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.

継続的インテグレーションフローのテストをスピードアップしたい場合に非常に役立ちます。

1
Karim N Gorjux

テスト、開発、および本番用に異なるイニシャライザーモジュールがある場合は、イニシャライザーで何かを無効にするかリダイレクトできます。 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,
    },
}

次に、test.pyでこれを持っています:

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

これにより、コンソールハンドラーがFileHandlerに置き換えられ、引き続きログを取得することができますが、運用コードベースに触れる必要はありません。

pytestを使用している場合:

Pytestはログメッセージをキャプチャし、失敗したテストについてのみ表示するため、通常はログを無効にしたくないでしょう。代わりに、テスト用に別のsettings.pyファイル(たとえば、test_settings.py)を使用して追加します。

LOGGING_CONFIG = None

これはDjango=ロギングの設定を完全にスキップするように指示します。LOGGING設定は無視され、設定から削除できます。

このアプローチを使用すると、合格したテストのログは取得されず、失敗したテストのすべての利用可能なログが取得されます。

テストは、pytestによって設定されたロギングを使用して実行されます。 pytestの設定(たとえば、tox.ini)で好みに合わせて設定できます。デバッグレベルのログメッセージを含めるには、log_level = DEBUG(または対応するコマンドライン引数)を使用します。

0
Roger Dahl

私の場合、設定ファイルがありますsettings/test.pyテスト目的で特別に作成されたもので、次のようなものです。

from .base import *

DATABASES = {
    'default': {
        'ENGINE': 'Django.db.backends.sqlite3',
        'NAME': 'test_db'
    }
}

PASSWORD_HASHERS = (
    'Django.contrib.auth.hashers.MD5PasswordHasher',
)

LOGGING = {}

環境変数Django_SETTINGS_MODULE=settings.testから/etc/environment

0