Django:你如何在测试期间覆盖logfile变量?

时间:2013-06-10 13:31:19

标签: django django-testing django-settings django-login

我正在尝试让我的测试套件创建一个不同于我在开发中使用的日志文件,但由于某种原因,override_settings装饰器似乎不起作用。当我运行测试时,会写入相同的'project / project / debug_logfile'。我搞砸了哪里?

# settings.py
...
LOGFILE = ROOT + '/debug_logfile'

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
        'standard': {
             'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
             'datefmt' : "%d/%b/%Y %H:%M:%S"
        },
    },
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'handlers': {
        'null': {
            'level': 'DEBUG',
            'class': 'django.utils.log.NullHandler',
        },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
            'filters': ['require_debug_false']
        },
        'logfile': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': LOGFILE,
            'maxBytes': 50000,
            'backupCount': 2,
            'formatter': 'standard',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['null'],
            'propagate': True,
            'level': 'INFO',
        },
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': False,
        },
        'clients': {
            'handlers': ['console', 'logfile'],
            'level': 'DEBUG',
        },
        # display the db queries
        #'django.db.backends': {
        #    'handlers': ['console'],
        #    'level': 'DEBUG',
        #}
    }
}

# clients.management.commands.remindmanagers.py

class Command(BaseCommand):
    help = 'Creates task reminders and send emails to account and senior \
            managers when their client\'s contracts are about to expire'

    def handle(self, *args, **kwargs):
        three_months = datetime.date.today() + datetime.timedelta(days=90)
        # get all contracts that will be completed in the next 3 months
        contracts = Contract.objects.filter(finish__lte=three_months
                                   ).exclude(notices_left=0)

        if not contracts.exists():
            log.info('Tried running but there are no contracts about to expire.')
            return 0
        ...

# tests.test_clients

...
from django.core.management import call_command

@override_settings(LOGFILE=settings.ROOT + "/test_logfile")
class ClientCommandTest(BaseTestCase):
    def _file_exists(file_path):
        return os.path.exists(file_path)

    def test_remindmanagers_no_contracts(self):
        args = []
        kwargs = {}
        #self.assertFalse()
        # since there are no contracts yet, this should create an entry in ROOT + /logfile
        call_command('remindmanagers', *args, **kwargs)                                      # this should log in project/project/test_logfile not debug_logfile

2 个答案:

答案 0 :(得分:1)

覆盖设置是不够的。 因为日志记录是在 override_settings 生效之前配置的。 呼叫 configure_logging 更改日志记录设置,不要忘记返回原始设置。

看下面的例子:

settings.py

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'formatter1': {
            'format': 'settings logger: {message}',
            'style': '{',
        },
    },
    'handlers': {
        'stream': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'formatter1',
        },
    },
    'loggers': {
        'test_logging_override': {
            'handlers': ['stream'],
            'level': 'DEBUG',
            'propogate': True,
        },
    },
}

tests.py

from django.test import TestCase, override_settings

class TestLoggingOverride(TestCase):
    def test_logging_override(self):
        logger = logging.getLogger('test_logging_override')
        logger.debug('one')  # settings logger: one
        with override_settings(LOGGING={
            'version': 1,
            'disable_existing_loggers': False,
            'formatters': {
                'formatter1': {
                    'format': 'overridden logger: {message}',  # !!!!
                    'style': '{',
                },
            },
            'handlers': {
                'stream': {
                    'level': 'DEBUG',
                    'class': 'logging.StreamHandler',  # 'class': 'logging.FileHandler'
                    # 'filename': 'path/to/file',  # in case of FileHandler
                    'formatter': 'formatter1',
                },
            },
            'loggers': {
                'test_logging_override': {
                    'handlers': ['stream'],
                    'level': 'DEBUG',
                    'propogate': True,
                },
            },
        }):
            logger.debug('two')  # settings logger: two
            configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)  # really change settings
            logger.debug('three')  # overridden logger: three
        logger.debug('four')  # overridden logger: four
        configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)  # return to native settings
        logger.debug('five')  # settings logger: five

此外,您可以编写测试记录。

from unittest.mock import MagicMock, patch, call

class TestLoggingOverride(TestCase):
    @patch(
         target='sys.stderr',
         wraps=sys.stderr,  # wraps all of sys.stderr. Remove this line, if you want to disable writing to stderr in this test
     )
     def test_patched_stream_logger(self, mocked_stderr):
         logger = logging.getLogger('test_logging_override')
         with override_settings(LOGGING={
             'version': 1,
             'disable_existing_loggers': False,
             'formatters': {
                 'formatter1': {
                     'format': 'overridden logger: {message}',  # !!!!
                     'style': '{',
                 },
             },
             'handlers': {
                 'stream': {
                     'level': 'DEBUG',
                     'class': 'logging.StreamHandler',
                     'formatter': 'formatter1',
                 },
             },
             'loggers': {
                 'test_logging_override': {
                     'handlers': ['stream'],
                     'level': 'DEBUG',
                     'propogate': True,
                 },
             },
         }):
             configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)  # really change settings
             logger.debug('lorem ipsum')
         configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)  # return to native settings
         self.assertEqual(
             mocked_stderr.method_calls,
             [call.flush(),
              call.flush(),
              call.write('overridden logger: lorem ipsum\n'),
              call.flush(),
              call.flush(),
              call.flush(),
              call.flush()]
         )
         # print(mocked_stderr.method_calls)

答案 1 :(得分:0)

您的LOGGING dict文字包含变量LOGFILE,在处理dict文字时会立即对其进行评估。通过覆盖LOGFILE,您无法更改LOGGING字典中的条目,因为LOGFILE字符串只是被替换;字符串本身没有变异,所以最终只能指向另一个字符串LOGFILE

我认为您应该能够使用新文件名覆盖日志文件处理程序,如下所示:

@override_settings(LOGGING={
  'handlers': {
    'logfile': {
       'filename': settings.ROOT + "/test_logfile"
    }
  }
})
class ClientCommandTest(BaseTestCase):
  ...