如何在运行Python Django单元测试时禁用日志记录?
我正在使用一个简单的单元测试工具来测试我的Django应用。
我的应用在settings.py文件中配置了一个基本的日志记录器,使用的是:
logging.basicConfig(level=logging.DEBUG)
在我的应用代码中也使用了:
logger = logging.getLogger(__name__)
logger.setLevel(getattr(settings, 'LOG_LEVEL', logging.DEBUG))
但是,当我运行单元测试时,我想关闭日志记录,这样就不会在测试结果中出现太多杂乱的信息。有没有简单的方法可以全局关闭日志记录,这样在我运行测试时,应用特定的日志记录器就不会把信息输出到控制台?
21 个回答
有没有简单的方法可以全局关闭日志记录,这样在我运行测试时,应用程序特定的日志就不会输出到控制台了?
其他的回答通过全局设置日志系统来忽略所有内容,从而防止“输出到控制台”。这个方法有效,但我觉得太粗暴了。我更倾向于做一些配置上的调整,只做必要的事情来防止日志输出到控制台。所以我在我的 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)会在测试期间捕获日志,并将相关的日志部分与测试失败一起输出。这对于找出测试失败的原因非常有用。如果完全关闭日志记录,那么就没有任何内容可以被捕获。
既然你在使用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', ...]
logging.disable(logging.CRITICAL)
logging.disable(logging.NOTSET)
这段代码会关闭所有低于或等于CRITICAL
级别的日志记录。也就是说,只有最严重的日志信息会被记录下来。你可以通过下面的方式重新开启日志记录。