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

发布于 2024-10-21 11:21:01 字数 405 浏览 8 评论 0原文

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

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

logging.basicConfig(level=logging.DEBUG)

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

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

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

I am using a simple unit test based test runner to test my Django application.

My application itself is configured to use a basic logger in settings.py using:

logging.basicConfig(level=logging.DEBUG)

And in my application code using:

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

However, when running unittests, I'd like to disable logging so that it doesn't clutter my test result output. Is there a simple way to turn off logging in a global way, so that the application specific loggers aren't writing stuff out to the console when I run tests?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(21

活雷疯 2024-10-28 11:21:01
logging.disable(logging.CRITICAL)

将禁用严重程度低于或等于 CRITICAL 的所有日志记录调用。可以使用以下命令重新启用日志记录

logging.disable(logging.NOTSET)
logging.disable(logging.CRITICAL)

will disable all logging calls with levels less severe than or equal to CRITICAL. Logging can be re-enabled with

logging.disable(logging.NOTSET)
飘然心甜 2024-10-28 11:21:01

由于您使用的是 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', ...]

Since you are in Django, you could add these lines to your settings.py:

import sys
import logging

if len(sys.argv) > 1 and sys.argv[1] == 'test':
    logging.disable(logging.CRITICAL)

That way you don't have to add that line in every setUp() on your tests.

You could also do a couple of handy changes for your test needs this way.

There is another "nicer" or "cleaner" way to add specifics to your tests and that is making your own test runner.

Just create a class like this:

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)

And now add to your settings.py file:

TEST_RUNNER = "PATH.TO.PYFILE.MyOwnTestRunner"
#(for example, 'utils.mytest_runner.MyOwnTestRunner')

This lets you do one really handy modification that the other approach doesn't, which is to make Django just tests the applications that you want. You can do that by changing the test_labels adding this line to the test runner:

if not test_labels:
    test_labels = ['my_app1', 'my_app2', ...]
狼亦尘 2024-10-28 11:21:01

有没有一种简单的方法可以以全局方式关闭日志记录,以便应用程序特定的记录器在我运行测试时不会将内容写入控制台?

其他答案通过全局设置日志记录基础设施以忽略任何内容来防止“将内容写入控制台”。这可行,但我发现这种方法太生硬了。我的方法是执行配置更改,该更改仅执行防止日志在控制台上泄露所需的操作。因此,我将 自定义日志记录过滤器 添加到我的 设置中.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)会在测试期间捕获日志,并将日志的相关部分与测试失败一起输出。它对于找出测试失败的原因很有用。如果日志记录完全关闭,则无法捕获任何内容。

Is there a simple way to turn off logging in a global way, so that the application specific loggers aren't writing stuff out to the console when I run tests?

The other answers prevent "writing stuff out to the console" by globally setting the logging infrastructure to ignore anything. This works but I find it too blunt an approach. My approach is to perform a configuration change which does only what's needed to prevent logs to get out on the console. So I add a custom logging filter to my 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

And I configure the Django logging to use the filter:

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

End result: when I'm testing, nothing goes to the console, but everything else stays the same.

Why Do This?

I design code that contains logging instructions that are triggered only in specific circumstances and that should output the exact data I need for diagnosis if things go wrong. Therefore I test that they do what they are supposed to do and thus completely disabling logging is not viable for me. I don't want to find once the software is in production that what I thought would be logged is not logged.

Moreover, some test runners (Nose, for instance) will capture logs during testing and output the relevant part of the log together with a test failure. It is useful in figuring out why a test failed. If logging is completely turned off, then there's nothing that can be captured.

素年丶 2024-10-28 11:21:01

我喜欢 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)

I like Hassek's custom test runner idea. It should be noted that DjangoTestSuiteRunner is no longer the default test runner in Django 1.6+, it has been replaced by the DiscoverRunner. For default behaviour, the test runner should be more like:

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)
冷血 2024-10-28 11:21:01

我发现,对于 unittest 或类似框架中的测试,安全禁用单元测试中不需要的日志记录的最有效方法是在 setUp/<特定测试用例的 code>tearDown 方法。这可以让我们专门针对应禁用日志的位置。您还可以在您正在测试的类的记录器上明确执行此操作。

import unittest
import logging

class TestMyUnitTest(unittest.TestCase):
    def setUp(self):
        logging.disable(logging.CRITICAL)

    def tearDown(self):
        logging.disable(logging.NOTSET)

I've found that for tests within unittest or similar a framework, the most effective way to safely disable unwanted logging in unit tests is to enable/disable in the setUp/tearDown methods of a particular test case. This lets one target specifically where logs should be disabled. You could also do this explicitly on the logger of the class you're testing.

import unittest
import logging

class TestMyUnitTest(unittest.TestCase):
    def setUp(self):
        logging.disable(logging.CRITICAL)

    def tearDown(self):
        logging.disable(logging.NOTSET)
謌踐踏愛綪 2024-10-28 11:21:01

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

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

I am using a simple method decorator to disable logging only in a particular test method.

def disable_logging(f):

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

        return result

    return wrapper

And then I use it as in the following example:

class ScenarioTestCase(TestCase):

    @disable_logging
    test_scenario(self):
        pass
萝莉病 2024-10-28 11:21:01

有一些漂亮且干净的方法可以使用 unittest.mock.patch 方法暂停测试中的日志记录。

foo.py

import logging


logger = logging.getLogger(__name__)

def bar():
    logger.error('There is some error output here!')
    return True

测试.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 测试将不会产生任何日志输出。

There is some pretty and clean method to suspend logging in tests with unittest.mock.patch method.

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())

And python3 -m unittest tests will produce no logging output.

苄①跕圉湢 2024-10-28 11:21:01

有时您需要日志,有时则不需要。我的 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.

如果您想加快持续集成流程的测试速度,这将非常有帮助。

Sometimes you want the logs and sometimes not. I have this code in my 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)

So if you run your test with the --no-logs options you'll get only the critical logs:

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

It's very helpful if you want speedup the tests on your continuous integration flow.

孤芳又自赏 2024-10-28 11:21:01

如果我希望暂时抑制特定的记录器,我已经编写了一个我发现有用的小上下文管理器:

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 完成。

In cases where I wish to temporarily suppress a specific logger, I've written a little context manager that I've found useful:

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

You then use it like:

class MyTestCase(TestCase):
    def test_something(self):
        with disable_logger('<logger name>'):
            # code that causes the logger to fire

This has the advantage that the logger is re-enabled (or set back to its prior state) once the with completes.

浪荡不羁 2024-10-28 11:21:01

禁用整个模块的日志记录:

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

Disable logging for the entire module:

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
年少掌心 2024-10-28 11:21:01

如果您使用的是 pytest:

由于 pytest 捕获日志消息并且仅在失败的测试中显示它们,因此您通常不想禁用任何日志记录。相反,使用单独的 settings.py 文件进行测试(例如,test_settings.py),并向其中添加:

LOGGING_CONFIG = None

这告诉 Django 完全跳过配置日志记录。 LOGGING 设置将被忽略,并且可以从设置中删除。

使用这种方法,您不会获得通过的测试的任何日志记录,并且会获得失败的测试的所有可用日志记录。

测试将使用 pytest 设置的日志记录运行。可以根据您的喜好在 pytest 设置中对其进行配置(例如,tox.ini)。要包含调试级别日志消息,请使用 log_level = DEBUG (或相应的命令行参数)。

If you're using pytest:

Since pytest captures log messages and only displays them for failed tests, you typically don't want to disable any logging. Instead, use a separate settings.py file for tests (e.g., test_settings.py), and add to it:

LOGGING_CONFIG = None

This tells Django to skip configuring the logging altogether. The LOGGING setting will be ignored and can be removed from the settings.

With this approach, you don't get any logging for passed tests, and you get all available logging for failed tests.

The tests will run using the logging that was set up by pytest. It can be configured to your liking in the pytest settings (e.g., tox.ini). To include debug level log messages, use log_level = DEBUG (or the corresponding command line argument).

才能让你更想念 2024-10-28 11:21:01

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

# tests/unit/__init__.py
import logging

logging.disable(logging.CRITICAL)

You can put this in the top level directory for unit tests __init__.py file. This will disable logging globally in the unit test suite.

# tests/unit/__init__.py
import logging

logging.disable(logging.CRITICAL)
想你只要分分秒秒 2024-10-28 11:21:01

我的一些测试包含有关日志输出的断言,因此更改日志级别会破坏它们。相反,我更改了 Django LOGGING 设置以使用

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

Some of my tests contain assertions about log output, so changing the log level breaks them. Instead, I changed my Django LOGGING settings to use a NullHandler when running tests:

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',
        },
    }
}
薄凉少年不暖心 2024-10-28 11:21:01

如果您不希望在单元测试的 setUp() 和 TeaDown() 中重复打开/关闭它(看不到原因),您可以每个类执行一次:

    import unittest
    import logging

    class TestMyUnitTest(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            logging.disable(logging.CRITICAL)
        @classmethod
        def tearDownClass(cls):
            logging.disable(logging.NOTSET)

If you don't want it repeatedly turn it on/off in setUp() and tearDown() for unittest (don't see the reason for that), you could just do it once per class:

    import unittest
    import logging

    class TestMyUnitTest(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            logging.disable(logging.CRITICAL)
        @classmethod
        def tearDownClass(cls):
            logging.disable(logging.NOTSET)
超可爱的懒熊 2024-10-28 11:21:01

bh

对我来说效果很好 - 在 conftest.py 中:

 import confing
 # disable logging for tests
 [logging.disable(level) for level in [logging.DEBUG,
                                       logging.INFO,
                                       logging.ERROR,
                                       logging.CRITICAL]]

b.h.

for me works whell - in conftest.py:

 import confing
 # disable logging for tests
 [logging.disable(level) for level in [logging.DEBUG,
                                       logging.INFO,
                                       logging.ERROR,
                                       logging.CRITICAL]]
终陌 2024-10-28 11:21:01

就我而言,我有一个专门为测试目的创建的设置文件 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

In my case I have a settings file settings/test.py created specifically for testing purposes, here's what it looks like:

from .base import *

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

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

LOGGING = {}

I put an environment variable DJANGO_SETTINGS_MODULE=settings.test to /etc/environment.

零度℉ 2024-10-28 11:21:01

如果您有不同的初始化程序模块用于测试、开发和生产,那么您可以禁用任何内容或在初始化程序中重定向它。我有 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 替换了控制台处理程序,并且意味着仍然可以进行日志记录,但我不必接触生产代码库。

If you have different initaliser modules for test, dev and production then you can disable anything or redirect it in the initialser. I have local.py, test.py and production.py that all inherit from common.y

common.py does all the main config including this snippet :

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

Then in test.py I have this:

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

This replaces the console handler with a FileHandler and means still get logging but I do not have to touch the production code base.

往事风中埋 2024-10-28 11:21:01

如果您使用 pytest,您可以安装超级有用的 pytest-mock 插件并定义一个自动使用的、会话范围内的固定装置,可以由环境变量触发:

# 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")

In case you're using pytest, you can install the super useful pytest-mock plugin and define an auto-used, session-scoped fixture that can be triggered by an 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")
冷默言语 2024-10-28 11:21:01

我们使用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,
    ]
    ...

)

We use structlog, which is a bit more complicated to disable:

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,
    ]
    ...

)
好多鱼好多余 2024-10-28 11:21:01
"""Module for custom test runner."""

import logging

from django.conf import settings
from django.test.runner import DiscoverRunner


class FastTestRunner(DiscoverRunner):
    """Custom Django test runner to speed up the test suite."""

    def setup_test_environment(self):
        """Override the setup_test_environment method to customize the test environment.

        This method is called before the test suite runs and is used to set up any necessary configurations.

        In this custom runner, we disable file writes during tests by changing the default storage backend
        to use Django's InMemoryStorage, which does not write files to the filesystem. Additionally, we
        use a faster password hasher (MD5) to speed up test cases involving user authentication.
        """
        super().setup_test_environment()

        # Disable file writes during tests by using Django's FileSystemStorage
        settings.STORAGES = {"default": {"BACKEND": "django.core.files.storage.InMemoryStorage"}}

        # Use a faster password hasher (MD5) to speed up test cases involving user authentication
        settings.PASSWORD_HASHERS = ("django.contrib.auth.hashers.MD5PasswordHasher",)

        # disable logging below CRITICAL while testing
        logging.disable(logging.CRITICAL)
"""Module for custom test runner."""

import logging

from django.conf import settings
from django.test.runner import DiscoverRunner


class FastTestRunner(DiscoverRunner):
    """Custom Django test runner to speed up the test suite."""

    def setup_test_environment(self):
        """Override the setup_test_environment method to customize the test environment.

        This method is called before the test suite runs and is used to set up any necessary configurations.

        In this custom runner, we disable file writes during tests by changing the default storage backend
        to use Django's InMemoryStorage, which does not write files to the filesystem. Additionally, we
        use a faster password hasher (MD5) to speed up test cases involving user authentication.
        """
        super().setup_test_environment()

        # Disable file writes during tests by using Django's FileSystemStorage
        settings.STORAGES = {"default": {"BACKEND": "django.core.files.storage.InMemoryStorage"}}

        # Use a faster password hasher (MD5) to speed up test cases involving user authentication
        settings.PASSWORD_HASHERS = ("django.contrib.auth.hashers.MD5PasswordHasher",)

        # disable logging below CRITICAL while testing
        logging.disable(logging.CRITICAL)
筑梦 2024-10-28 11:21:01

如果您在 2021 年或以后想知道这个问题,您可能会问错问题

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

因此,如果您使用的记录器与您在 LOGGING['loggers'] 中定义的记录器匹配并由 处理>“console” 处理程序,测试不应在屏幕上生成任何日志记录。

如果您在测试中看到某些内容,则

  • 说明您的记录器名称与您在 settings.LOGGING 中定义的内容不匹配,
  • 正在使用 DEBUG=True 运行测试(需要覆盖)
  • 已从控制台处理程序的过滤器中删除了“require_debug_true”

*现代版本含义:2.1 及更高版本,即不是古代的。

You're likely asking the wrong question if you're wondering about this in 2021 or later

On modern versions of Django*, with the out of the box configuration, tests should not produce any logging on screen. Thus, if you're aksing this question the real answer may be "something is misconfigured". This is because (by default):

Thus, if the loggers you use match those you have defined in LOGGING['loggers'] and being handled by the "console" handler, tests should not produce any logging on screen.

If you see something in tests anyway, you either

  • have mismatched the name of your logger with the thing you've defined in settings.LOGGING
  • are running tests with DEBUG=True (which requires an override)
  • have removed "require_debug_true" from your console handler's filters.

*Modern versions meaning: 2.1 and up, i.e. not ancient.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文