Модуль ведения журнала Python: как сохранить журнал в файл, если (и только если) проверка утверждения завершается неудачей?

#python #python-3.x #python-2.7 #unit-testing #logging

#python #python-3.x #python-2.7 #модульное тестирование #ведение журнала

Вопрос:

Я ищу элегантное и питоновское решение, чтобы тесты сохраняли журнал в файл, но только в случае сбоя теста. Я хотел бы упростить задачу и придерживаться встроенного logging модуля Python.

Мое текущее решение — использовать функцию-оболочку для утверждения каждого теста:

 import unittest

class superTestCase(unittest.TestCase): 
    ...

    def assertWithLogging(self, assertion, assertion_arguments, expected_response, actual_response, *args):
        try:
            assertion(*assertion_arguments)
        except AssertionError as ae:
            test_name = inspect.stack()[1][3]
            current_date_time = datetime.datetime.now().strftime("%Y.%m.%d %H-%M-%S")
            logging.basicConfig(filename='tests/{}-{}-Failure.log'.format(current_date_time, test_name),
                                filemode='a',
                                format='%(message)s',
                                level=logging.DEBUG
                                )
            logger = logging.getLogger('FailureLogger')
            logger.debug('{} has failed'.format(test_name))
            logger.debug('Expected response(s):')
            logger.debug(expected_response)
            logger.debug('Actual response:')
            logger.debug(actual_response)
            for arg in args:
                logger.debug('Additionl logging info:')
                logger.debug(arg)
            raise ae

    def testSomething(self):
        ...

        self.assertWithLogging(self.assertEqual,
                               [expected_response, actual_response]
                               expected_response,
                               actual_response,
                               some_other_variable
                               )
  

Хотя это работает так, как я ожидаю, это решение кажется мне неуклюжим и не слишком питоническим.

  1. Какой был бы (есть) более элегантный способ добиться того же результата?
  2. Каковы недостатки текущего подхода?

Комментарии:

1. Я не вижу никакого способа переопределить поведение всех различных методов assert, поэтому функция-оболочка может быть лучшим способом. Тем не менее: (1) Вы должны полностью удалить basicConfig из этого модуля (он даже ничего не сделает, если корневой регистратор уже настроен) и переместить вызов getLogger на уровень модуля; (2) Вам не нужно фиксировать текущее время, поскольку вызовы debug уже производят это ( asctime поле); (3) Я не вижу необходимости повторять expected_response и actual_response — это просто делает каждый вызов assertWithLogging более подробным.

2. @JoeP, спасибо за комментарий. У меня есть несколько последующих вопросов: (1) Вы хотите перейти basicConfig к отдельному модулю (вместе с import logging , очевидно), а затем импортировать этот модуль туда, где требуется logger? (2) Я использую текущее время, чтобы сделать его частью имени файла журнала. Можно ли использовать asctime как часть basicConfig(filename=...) ? (3) Предлагаете ли вы вместо этого регистрировать сообщение об утверждении? Потому что теперь, если я удалю ведение expected_response журнала и actual_response , someTest has failed останется только.

Ответ №1:

Вы можете использовать различные механизмы ведения журнала, в которых вы можете установить тип журналов, которые вы хотите получить.

В приведенном ниже журнале будут регистрироваться только сообщения об ошибках.

 Logger.error(msg, *args, **kwargs)
  

Это регистрируется msg с уровнем logging.ERROR в этом регистраторе. Аргументы интерпретируются как for debug() .

Комментарии:

1. Спасибо за ваш ответ. Я, вероятно, был недостаточно точен в том, о чем спрашивал. Я ищу способ регистрировать некоторые сообщения (фактически сохранять журнал в файл, что в данном случае можно считать эквивалентным), только если утверждение теста завершается неудачно (независимо от сообщения logging.level ). Поскольку я не могу добавлять logger вызовы внутри самого утверждения, я придумал решение-оболочку, представленное в вопросе. Хотя я чувствую, что это неуклюже, поэтому я ищу лучшую альтернативу. Надеюсь, теперь вопрос имеет больше смысла.