我正在使用一个简单的基于单元测试的测试运行器来测试我的Django应用程序。
我的应用程序本身配置为使用settings.py中的基本记录器:
logging.basicConfig(level=logging.DEBUG)
在我的应用程序代码中使用:
logger = logging.getLogger(__name__)
logger.setLevel(getattr(settings, 'LOG_LEVEL', logging.DEBUG))
但是,在运行unittests时,我想禁用日志记录,以免它使我的测试结果输出混乱。是否有一种以全局方式关闭日志记录的简单方法,以便特定于应用程序的记录器在运行测试时不会将内容写入控制台?
答案 0 :(得分:221)
logging.disable(logging.CRITICAL)
将禁用级别低于或等于CRITICAL
的所有日志记录调用。可以使用
logging.disable(logging.NOTSET)
答案 1 :(得分:41)
由于你在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')
这可以让你做一个非常方便的修改,而另一种方法却没有,这就是让Django只测试你想要的应用程序。您可以通过更改test_labels将此行添加到测试运行器来实现:
if not test_labels:
test_labels = ['my_app1', 'my_app2', ...]
答案 2 :(得分:20)
我喜欢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)
答案 3 :(得分:19)
是否有一种以全局方式关闭日志记录的简单方法,以便特定于应用程序的记录器在运行测试时不会将内容写入控制台?
其他答案通过全局设置日志记录基础结构以忽略任何内容来阻止“将内容写入控制台”。这有效,但我发现这种做法过于直率。我的方法是执行配置更改,该配置更改仅执行阻止日志在控制台上运行所需的内容。所以我向settings.py
添加了custom logging filter:
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
我configure the 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,
},
}
}
最终结果:当我测试时,没有任何东西进入控制台,但其他一切都保持不变。
我设计的代码包含仅在特定情况下触发的记录指令,如果出现问题,应输出诊断所需的确切数据。因此,我测试他们做了他们应该做的事情,因此完全禁用日志记录对我来说是不可行的。一旦软件投入生产,我不想发现我记录的思想将被记录下来。
此外,一些测试运行器(例如,Nose)将在测试期间捕获日志并输出日志的相关部分以及测试失败。它有助于确定测试失败的原因。如果完全关闭日志记录,则无法捕获任何内容。
答案 4 :(得分:3)
使用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
将不会产生日志输出。
答案 5 :(得分:3)
我发现对于unittest
或类似框架内的测试,在单元测试中安全禁用不需要的日志记录的最有效方法是在setUp
/ {{1}中启用/禁用特定测试用例的方法。这使得一个目标特别适用于禁用日志的位置。您也可以在正在测试的类的记录器上显式执行此操作。
tearDown
答案 6 :(得分:1)
有时您需要日志,有时则不需要。我在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.
如果您希望加速持续集成流程的测试,那将非常有用。
答案 7 :(得分:1)
我正在使用一个简单的方法装饰器来仅在特定的测试方法中禁用日志记录。
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
答案 8 :(得分:1)
在我希望暂时禁止使用特定记录器的情况下,我编写了一个有用的小上下文管理器:
from contextlib import contextmanager
import logging
@contextmanager
def disable_logger(name):
"""Temporarily disable a specific logger."""
logger = logging.getLogger(name)
old_value = logger.disabled
logger.disabled = True
try:
yield
finally:
logger.disabled = old_value
然后,您可以像这样使用它:
class MyTestCase(TestCase):
def test_something(self):
with disable_logger('<logger name>'):
# code that causes the logger to fire
这样做的好处是,with
完成后,记录器将重新启用(或重新设置为以前的状态)。
答案 9 :(得分:1)
您可以将其放在单元测试__init__.py
文件的顶级目录中。这将禁用单元测试套件中的全局日志记录。
# tests/unit/__init__.py
import logging
logging.disable(logging.CRITICAL)
答案 10 :(得分:0)
b.h.
对我来说很有效 - 在 conftest.py 中:
import confing
# disable logging for tests
[logging.disable(level) for level in [logging.DEBUG,
logging.INFO,
logging.ERROR,
logging.CRITICAL]]
答案 11 :(得分:0)
禁用整个模块的日志记录:
import logging
def setUpModule():
"""Disable logging while doing these tests."""
logging.disable()
def tearDownModule():
"""Re-enable logging after doing these tests."""
logging.disable(logging.NOTSET)
class TestFoo(unittest.TestCase):
def test_foo(self):
pass
答案 12 :(得分:0)
如果您使用的是 pytest,您可以安装超级有用的 pytest-mock 插件并定义一个自动使用的、会话范围的装置,该装置可以由 env var 触发:
# conftest.py
import os
import pytest
@pytest.fixture(autouse=True, scope="session")
def _shut_logger(session_mocker):
if os.getenv("SHUT_LOGGER", None):
return session_mocker.patch("foo.logger")
答案 13 :(得分:0)
我的一些测试包含有关日志输出的断言,因此更改日志级别会破坏它们。相反,我在运行测试时将Django LOGGING
设置更改为使用NullHandler:
if 'test' in sys.argv:
_LOG_HANDLERS = ['null']
else:
_LOG_HANDLERS = ['console']
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'simple': {
'format': '%(levelname)s %(message)s'
},
},
'handlers': {
'null': {
'level': 'DEBUG',
'class': 'logging.NullHandler',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple',
},
},
'loggers': {
'django': {
'handlers': _LOG_HANDLERS,
'propagate': True,
'level': 'INFO',
},
}
}
答案 14 :(得分:0)
如果您使用的是pytest
:
由于pytest捕获日志消息并仅在失败的测试中显示它们,因此您通常不想禁用任何日志记录。相反,请使用单独的settings.py
文件进行测试(例如test_settings.py
),然后添加到文件中:
LOGGING_CONFIG = None
这告诉Django完全跳过配置日志记录。 LOGGING
设置将被忽略,可以从设置中删除。
使用这种方法,对于通过的测试,您不会获得任何日志记录,对于失败的测试,您将获得所有可用的日志记录。
将使用pytest
设置的日志记录运行测试。您可以根据自己的喜好在pytest
设置(例如tox.ini
)中进行配置。要包含调试级别的日志消息,请使用log_level = DEBUG
(或相应的命令行参数)。
答案 15 :(得分:0)
如果您有用于测试,开发和生产的其他初始化模块,则可以禁用任何内容或将其重定向到初始化程序中。我有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替换了控制台处理程序,意味着仍然可以记录日志,但是我不必接触生产代码库。
答案 16 :(得分:0)
如果您不希望它在setUp()和tearDown()中重复打开/关闭它以进行单元测试(看不到其原因),则每个类只能执行一次:
import unittest
import logging
class TestMyUnitTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
logging.disable(logging.CRITICAL)
@classmethod
def tearDownClass(cls):
logging.disable(logging.NOTSET)
答案 17 :(得分:0)
在我的情况下,我有一个专门为测试目的而创建的设置文件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
。