如何在Python Django中运行单元测试时禁用日志记录?

shr*_*ddd 160 python django logging unit-testing

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

我的应用程序本身配置为使用settings.py中的基本记录器:

logging.basicConfig(level=logging.DEBUG)
Run Code Online (Sandbox Code Playgroud)

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

logger = logging.getLogger(__name__)
logger.setLevel(getattr(settings, 'LOG_LEVEL', logging.DEBUG))
Run Code Online (Sandbox Code Playgroud)

但是,在运行单元测试时,我想禁用日志记录,以免它使我的测试结果输出混乱.是否有一种简单的方法可以以全局方式关闭日志记录,以便特定于应用程序的记录器在运行测试时不会将内容写入控制台?

unu*_*tbu 237

logging.disable(logging.CRITICAL)
Run Code Online (Sandbox Code Playgroud)

将禁用所有日志级别低于或等于的日志调用CRITICAL.可以使用重新启用日志记录

logging.disable(logging.NOTSET)
Run Code Online (Sandbox Code Playgroud)

  • 这可能是显而易见的但我觉得有时候为了其他读者的利益而说明显而易见:你可以在应用程序的`tests.py`顶部调用`logging.disable`(来自接受的答案)那是在做日志记录. (36认同)
  • 将它放在`tests`模块的__init__.py中是非常有用的. (30认同)
  • 在你的`tearDown()`方法中:`logging.disable(logging.NOTSET)`将日志记录整齐地放回原位. (9认同)
  • 我最终把调用放在setUp()中,但你的观点很好. (6认同)

Has*_*sek 43

由于你在Django,你可以将这些行添加到settings.py:

import sys
import logging

if len(sys.argv) > 1 and sys.argv[1] == 'test':
    logging.disable(logging.CRITICAL)
Run Code Online (Sandbox Code Playgroud)

这样您就不必在测试的每个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)
Run Code Online (Sandbox Code Playgroud)

现在添加到settings.py文件:

TEST_RUNNER = "PATH.TO.PYFILE.MyOwnTestRunner"
#(for example, 'utils.mytest_runner.MyOwnTestRunner')
Run Code Online (Sandbox Code Playgroud)

这让你可以做一个非常方便的修改,而另一种方法却没有,这就是让Django只测试你想要的应用程序.您可以通过更改test_labels将此行添加到测试运行器来实现:

if not test_labels:
    test_labels = ['my_app1', 'my_app2', ...]
Run Code Online (Sandbox Code Playgroud)

  • 对于Django 1.6+,请查看@alukach的答案. (6认同)
  • 有时在单元测试中,我想断言记录了错误,因此这种方法并不理想。尽管如此,它*是*一个很好的答案。 (2认同)

Lou*_*uis 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
Run Code Online (Sandbox Code Playgroud)

配置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,
        },
    }
}
Run Code Online (Sandbox Code Playgroud)

最终结果:当我测试时,没有任何东西进入控制台,但其他一切都保持不变.

为什么这样?

我设计的代码包含仅在特定情况下触发的日志记录指令,如果出现问题,应输出诊断所需的确切数据.因此,我测试他们做了他们应该做的事情,因此完全禁用日志记录对我来说是不可行的.一旦软件投入生产,我不想找到我认为将记录的内容.

此外,一些测试运行器(例如,Nose)将在测试期间捕获日志并输出日志的相关部分以及测试失败.它有助于确定测试失败的原因.如果完全关闭日志记录,则无法捕获任何内容.


alu*_*ach 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)
Run Code Online (Sandbox Code Playgroud)


Edu*_*ans 9

我正在使用一个简单的方法装饰器来仅在特定的测试方法中禁用日志记录。

def disable_logging(f):

    def wrapper(*args):
        logging.disable(logging.CRITICAL)
        result = f(*args)
        logging.disable(logging.NOTSET)

        return result

    return wrapper
Run Code Online (Sandbox Code Playgroud)

然后我按照以下示例使用它:

class ScenarioTestCase(TestCase):

    @disable_logging
    test_scenario(self):
        pass
Run Code Online (Sandbox Code Playgroud)


mcg*_*uip 8

我发现对于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)
Run Code Online (Sandbox Code Playgroud)


val*_*lex 5

有一些漂亮而干净的方法可以使用unittest.mock.patch方法暂停登录测试。

foo.py :

import logging


logger = logging.getLogger(__name__)

def bar():
    logger.error('There is some error output here!')
    return True
Run Code Online (Sandbox Code Playgroud)

测试.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())
Run Code Online (Sandbox Code Playgroud)

并且python3 -m unittest tests不会产生日志输出。