我正在使用一个简单的基于单元测试的测试运行器来测试我的Django应用程序。

我的应用程序本身被配置为在settings.py中使用一个基本的日志记录器:

logging.basicConfig(level=logging.DEBUG)

并在我的应用程序代码使用:

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

但是,在运行单元测试时,我想禁用日志记录,这样它就不会使测试结果输出混乱。有没有一种简单的方法以全局方式关闭日志记录,这样当我运行测试时,特定于应用程序的日志记录器就不会把东西写到控制台了?


当前回答

你可以把它放在单元测试的顶层目录__init__.py文件中。这将在单元测试套件中禁用全局日志记录。

# tests/unit/__init__.py
import logging

logging.disable(logging.CRITICAL)

其他回答

如果你想在2021年或以后考虑这个问题,你可能问错了问题

在现代版本的Django*中,使用开箱即用的配置,测试应该不会在屏幕上产生任何日志记录。因此,如果你在问这个问题,真正的答案可能是“某些东西配置错误”。这是因为(默认情况下):

打印日志消息到控制台需要debug为True 测试运行DEBUG=False

因此,如果您使用的记录器与在LOGGING['loggers']中定义的记录器相匹配,并且由“控制台”处理程序处理,那么测试应该不会在屏幕上产生任何日志记录。

如果你在测试中看到了什么,你也

日志记录器的名称与在设置中定义的内容不匹配。日志记录 正在使用DEBUG=True运行测试(需要重写) 已经从控制台处理程序的过滤器中删除了“require_debug_true”。

*现代版本的意思是:2.1及以上,即不古老。

如果你有不同的初始化器模块用于测试,开发和生产,那么你可以禁用任何东西或重定向初始化器。我有local。py, test。py和production。py,它们都继承自common。y

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,这意味着仍然可以获得日志记录,但我不必触及生产代码库。

有时你想要日志,有时不想要。我在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选项运行测试,你将只得到关键日志:

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

如果您想加快持续集成流的测试,这是非常有用的。

因为你在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', ...]

我们使用structlog,禁用它有点复杂:

from structlog import DropEvent

def disable_logging_in_tests(_, __, event_dict):
    if len(sys.argv) > 1 and sys.argv[1] == 'test':
        raise DropEvent
    return event_dict


structlog.configure(
    processors=[
        ...
        disable_logging_in_tests,
    ]
    ...

)