पायथन Django में यूनिट परीक्षण चलाने के दौरान मैं लॉगिंग को कैसे अक्षम कर सकता हूं?


168

मैं अपने Django एप्लिकेशन का परीक्षण करने के लिए एक साधारण इकाई परीक्षण आधारित परीक्षण धावक का उपयोग कर रहा हूं।

मेरा एप्लिकेशन स्वयं सेटिंग्स में एक मूल लकड़हारा का उपयोग करने के लिए कॉन्फ़िगर किया गया है:

logging.basicConfig(level=logging.DEBUG)

और मेरे एप्लिकेशन कोड का उपयोग करके:

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

हालांकि, जब unittests चल रहा है, तो मैं लॉगिंग को अक्षम करना चाहूंगा ताकि यह मेरे परीक्षा परिणाम आउटपुट को अव्यवस्थित न करे। क्या वैश्विक तरीके से लॉगिंग बंद करने का एक सरल तरीका है, ताकि परीक्षण चलाने के दौरान एप्लिकेशन विशिष्ट लॉगर्स कंसोल पर सामान नहीं लिख रहे हैं?


आपने परीक्षण चलाने के दौरान लॉगिंग को कैसे सक्षम किया? और आप django LOGGING का उपयोग क्यों नहीं कर रहे हैं?
प्रातः

जवाबों:


249
logging.disable(logging.CRITICAL)

सभी लॉगिंग कॉल को उन स्तरों के साथ अक्षम कर देगा, जो कम से कम गंभीर या इसके बराबर हैं CRITICAL। लॉगिंग को फिर से सक्षम किया जा सकता है

logging.disable(logging.NOTSET)

42
यह स्पष्ट हो सकता है लेकिन मुझे यह कभी-कभी उपयोगी लगता है कि अन्य पाठकों के लाभ के लिए स्पष्ट रूप से बताएं: आप अपने आवेदन logging.disableके शीर्ष पर उस कॉल को डाल देंगे tests.pyजो लॉगिंग कर रही है।
सीजे गेकोनेट

7
मैंने कॉल को सेटअप () में डाल दिया, लेकिन आपकी बात अच्छी तरह से हो गई।
shreddd

अपने परीक्षण के सेटअप () विधि में, या वास्तविक परीक्षण में जो लॉग संदेश उत्पन्न करता है जिसे आप छिपाना चाहते हैं।
15

10
और अपने tearDown()तरीके में: logging.disable(logging.NOTSET)बड़े करीने से लॉगिंग को वापस रखता है।
मिलीलीटर

34
मॉड्यूल के init .py में इसे डालना testsबहुत उपयोगी है।
तोबी

46

चूँकि आप Django में हैं, आप इन सेटिंग्स को अपनी सेटिंग्स में जोड़ सकते हैं:

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)

और अब अपनी सेटिंग्स में जोड़ें। फ़ाइल:

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

ज़रूर - इसे सेटिंग्स में डालने के बाद यह ग्लोबल हो जाएगा।
shreddd

7
Django 1.6+ के लिए कृपया @alukach उत्तर जांचें।
हस्सेक

2
कभी-कभी यूनिट परीक्षणों में, मैं यह दावा करना चाहता हूं कि एक त्रुटि लॉग की गई थी इस प्रकार यह विधि आदर्श नहीं है। फिर भी, यह है एक अच्छा जवाब।
सारथ्रियन - एसई दुरुपयोग के खिलाफ

23

क्या वैश्विक तरीके से लॉगिंग बंद करने का एक सरल तरीका है, ताकि परीक्षण चलाने के दौरान एप्लिकेशन विशिष्ट लॉगर्स कंसोल पर सामान नहीं लिख रहे हैं?

अन्य उत्तर किसी भी चीज को नजरअंदाज करने के लिए लॉगिंग इन्फ्रास्ट्रक्चर को सेट करके विश्व स्तर पर "कंसोल के बाहर सामान लिखने" को रोकते हैं। यह काम करता है, लेकिन मुझे लगता है कि यह एक दृष्टिकोण को कुंद कर देता है। मेरा दृष्टिकोण एक कॉन्फ़िगरेशन परिवर्तन करना है जो केवल कंसोल पर बाहर निकलने के लिए लॉग को रोकने के लिए क्या आवश्यक है। इसलिए मैं अपने लिए एक कस्टम लॉगिंग फ़िल्टर जोड़ता हूं 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,
        },
    }
}

अंतिम परिणाम: जब मैं परीक्षण कर रहा हूं, तो कुछ भी कंसोल पर नहीं जाता है, लेकिन बाकी सब समान रहता है।

यह क्यों?

मैं कोड डिजाइन करता हूं जिसमें लॉगिंग निर्देश शामिल हैं जो केवल विशिष्ट परिस्थितियों में चालू होते हैं और अगर मुझे गलत तरीके से निदान के लिए आवश्यक सटीक डेटा का उत्पादन करना चाहिए। इसलिए मैं परीक्षण करता हूं कि वे वही करते हैं जो वे करने वाले हैं और इस तरह पूरी तरह से अक्षम करना मेरे लिए व्यवहार्य नहीं है। मुझे लगता है कि एक बार सॉफ्टवेयर उत्पादन में नहीं है, जो मैंने सोचा था कि लॉग किया जाएगा लॉग नहीं है।

इसके अलावा, कुछ परीक्षण धावकों (उदाहरण के लिए,) परीक्षण के दौरान लॉग पर कब्जा कर लेंगे और परीक्षण विफलता के साथ लॉग के संबंधित भाग को आउटपुट करेंगे। यह पता लगाने में उपयोगी है कि परीक्षण विफल क्यों हुआ। यदि लॉगिंग पूरी तरह से बंद है, तो ऐसा कुछ भी नहीं है जिसे कैप्चर किया जा सके।


"कोई भी परीक्षण धावक जो मैं उपयोग करता है वह Django कोड को इस तरह से लोड करेगा जो इसे सही बनाता है।" दिलचस्प ... कैसे?
वेबवॉकर्स

मेरे पास एक test_settings.pyफाइल है जो मेरे प्रोजेक्ट के बगल में है settings.py। इसे सेट करने के लिए लोड settings.pyऔर कुछ परिवर्तन करने के TESTING_MODEलिए सेट किया गया है True। मेरे परीक्षण धावकों को व्यवस्थित किया जाता है ताकि test_settingsDjango परियोजना सेटिंग्स के लिए लोड किया गया मॉड्यूल हो। इसके कई तरीके हो सकते हैं। मैं आमतौर पर वातावरण चर की स्थापना के साथ जाने DJANGO_SETTINGS_MODULEके लिए proj.test_settings
लुई

यह बहुत बढ़िया है और मुझे जैसा चाहिए वैसा ही करता है। जब तक कुछ विफल नहीं हो जाता है तब तक unittests के दौरान लॉगिंग को छुपाता है - फिर Django नोज आउटपुट को उठाता है और विफलता के साथ प्रिंट करता है। उत्तम। इकाई परीक्षण सक्रिय है या नहीं यह निर्धारित करने के लिए इसे साथ मिलाएं ।
रौज़ेना

21

मुझे हेसक के कस्टम टेस्ट रनर आइडिया पसंद हैं। यह ध्यान दिया जाना चाहिए कि 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)

मैंने बहुत सारी कोशिशों के बाद आपका समाधान पाया। हालाँकि, मैं चर TEST_RUNNER को सेटिंग में सेट करने में सक्षम नहीं हूँ क्योंकि यह उस मॉड्यूल को आयात करने में सक्षम नहीं है जहाँ test_runner फ़ाइल है।
बनी खरगोश

एक आयात मुद्दे की तरह लगता है। क्या आप TEST_RUNNER को धावक के लिए एक स्ट्रिंग पथ पर स्थापित कर रहे हैं (वास्तविक पायथन मॉड्यूल नहीं)? इसके अलावा, आपका रनर कहां स्थित है? मेरे पास एक अलग नाम का ऐप है helpers, जिसमें केवल ऐसे बर्तन हैं जो परियोजना के भीतर कहीं और से आयात नहीं करते हैं।
alukach

5

मैंने पाया है कि 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)

4

मैं केवल एक विशेष परीक्षण विधि में लॉगिंग को अक्षम करने के लिए एक सरल विधि डेकोरेटर का उपयोग कर रहा हूं।

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

3

विधि के साथ परीक्षणों में लॉगिंग को निलंबित करने के लिए कुछ सुंदर और स्वच्छ विधि है unittest.mock.patch

foo.py :

import logging


logger = logging.getLogger(__name__)

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

test.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कोई लॉगिंग आउटपुट नहीं देगा।


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.

यदि आप अपने निरंतर एकीकरण प्रवाह पर परीक्षणों को गति देना चाहते हैं तो यह बहुत उपयोगी है।


1

यदि आप नहीं चाहते हैं कि इसे बार-बार इसे सेटअप (/) और आंसू के लिए (बिना किसी कारण के) देखें तो (आप इसका कारण न देखें), आप इसे प्रति कक्षा एक बार कर सकते हैं:

    import unittest
    import logging

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

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हो जाता है।


1

आप इसे यूनिट परीक्षण __init__.pyफ़ाइल के लिए शीर्ष स्तर की निर्देशिका में रख सकते हैं । यह यूनिट टेस्ट सूट में विश्व स्तर पर लॉगिंग को अक्षम कर देगा।

# tests/unit/__init__.py
import logging

logging.disable(logging.CRITICAL)

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


0

यदि आपके पास परीक्षण, देव और उत्पादन के लिए अलग-अलग initaliser मॉड्यूल हैं तो आप किसी भी चीज को अक्षम कर सकते हैं या इसे शुरुआती में पुनर्निर्देशित कर सकते हैं। मेरे पास local.py, test.py और production.py है जो सभी को common.y से विरासत में मिले हैं

सामान्य थिसॉरस इस स्निपेट सहित सभी मुख्य विन्यास करता है:

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

यह फ़ाइलहैंडलर के साथ कंसोल हैंडलर को बदलता है और इसका मतलब है कि अभी भी लॉगिंग हो रही है लेकिन मुझे उत्पादन कोड आधार को छूने की आवश्यकता नहीं है।


0

यदि आप उपयोग कर रहे हैं pytest:

चूंकि पाइस्टेस्ट लॉग संदेशों को कैप्चर करता है और केवल उन्हें विफल परीक्षणों के लिए प्रदर्शित करता है, आप आमतौर पर किसी भी लॉगिंग को अक्षम नहीं करना चाहते हैं। इसके बजाय, settings.pyपरीक्षण के लिए एक अलग फ़ाइल का उपयोग करें (जैसे, test_settings.py), और इसे जोड़ें:

LOGGING_CONFIG = None

यह Django को लॉगिंग को पूरी तरह से कॉन्फ़िगर करना छोड़ना बताता है। LOGGINGसेटिंग नजरअंदाज कर दिया जाएगा और सेटिंग्स से हटाया जा सकता।

इस दृष्टिकोण के साथ, आपको उत्तीर्ण परीक्षणों के लिए कोई लॉगिंग नहीं मिलती है, और आपको असफल परीक्षणों के लिए सभी उपलब्ध लॉगिंग मिल जाती है।

परीक्षण उस लॉगिंग का उपयोग करके चलेगा जिसे इसके द्वारा सेट किया गया था pytest। इसे pytestसेटिंग्स (जैसे, tox.ini) में आपकी पसंद के अनुसार कॉन्फ़िगर किया जा सकता है । डिबग स्तर लॉग संदेश शामिल करने के लिए, log_level = DEBUG(या संबंधित कमांड लाइन तर्क) का उपयोग करें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.