2013-12-21 19 views
10

特定の条件下でlogger.warn('...')経由で警告を出すPythonコードのユニットテストを作成しようとしています。この警告がログに記録されたと主張するにはどうすればよいですか?私はassertLoggedが少なくともPython 3.4までは利用できないことに気付きました。残念ながら私は2.7です。Python 2.7ユニットテスト:ロガー警告がスローされました

答えて

2

ユニットテストの設定では、レコードをバッファリングするロギングハンドラを追加し、ティアダウン中に削除します。 Pythonテストインフラストラクチャの一部である基盤としてa couple of utility classes, TestHandler and Matcherを使用することができます。 (このリンクはPythonのデフォルトブランチにリンクしていますが、他のPythonバージョンではクラスを使用できるはずです)。これらのクラスの使用方法については、this postを参照してください。

2

Python 3.4 unittestに正確に追加されました。 TestCase.assertLogsを参照してください。 APIは、本当に使いやすいです。

with self.assertLogs('foo', level='INFO') as cm: 
    logging.getLogger('foo').info('first message') 
    logging.getLogger('foo.bar').error('second message') 
self.assertEqual(cm.output, ['INFO:foo:first message', 
          'ERROR:foo.bar:second message']) 

は今、この質問をpython2.7タグ付けされているが、それはpython + unittest + loggingのための同様のタイトルのときに、検索を表示されます。

# logger_test.py 
# this file contains the base class containing the newly added method 
# assertLogs 
import collections 
import logging 
_LoggingWatcher = collections.namedtuple("_LoggingWatcher", 
             ["records", "output"]) 

class _BaseTestCaseContext(object): 

    def __init__(self, test_case): 
     self.test_case = test_case 

    def _raiseFailure(self, standardMsg): 
     msg = self.test_case._formatMessage(self.msg, standardMsg) 
     raise self.test_case.failureException(msg) 


class _CapturingHandler(logging.Handler): 
    """ 
    A logging handler capturing all (raw and formatted) logging output. 
    """ 

    def __init__(self): 
     logging.Handler.__init__(self) 
     self.watcher = _LoggingWatcher([], []) 

    def flush(self): 
     pass 

    def emit(self, record): 
     self.watcher.records.append(record) 
     msg = self.format(record) 
     self.watcher.output.append(msg) 


class _AssertLogsContext(_BaseTestCaseContext): 
    """A context manager used to implement TestCase.assertLogs().""" 

    LOGGING_FORMAT = "%(levelname)s:%(name)s:%(message)s" 

    def __init__(self, test_case, logger_name, level): 
     _BaseTestCaseContext.__init__(self, test_case) 
     self.logger_name = logger_name 
     if level: 
      self.level = logging._levelNames.get(level, level) 
     else: 
      self.level = logging.INFO 
     self.msg = None 

    def __enter__(self): 
     if isinstance(self.logger_name, logging.Logger): 
      logger = self.logger = self.logger_name 
     else: 
      logger = self.logger = logging.getLogger(self.logger_name) 
     formatter = logging.Formatter(self.LOGGING_FORMAT) 
     handler = _CapturingHandler() 
     handler.setFormatter(formatter) 
     self.watcher = handler.watcher 
     self.old_handlers = logger.handlers[:] 
     self.old_level = logger.level 
     self.old_propagate = logger.propagate 
     logger.handlers = [handler] 
     logger.setLevel(self.level) 
     logger.propagate = False 
     return handler.watcher 

    def __exit__(self, exc_type, exc_value, tb): 
     self.logger.handlers = self.old_handlers 
     self.logger.propagate = self.old_propagate 
     self.logger.setLevel(self.old_level) 
     if exc_type is not None: 
      # let unexpected exceptions pass through 
      return False 
     if len(self.watcher.records) == 0: 
      self._raiseFailure(
       "no logs of level {} or higher triggered on {}" 
       .format(logging.getLevelName(self.level), self.logger.name)) 


class LogTestCase(unittest.TestCase): 

    def assertLogs(self, logger=None, level=None): 
     """Fail unless a log message of level *level* or higher is emitted 
     on *logger_name* or its children. If omitted, *level* defaults to 
     INFO and *logger* defaults to the root logger. 

     This method must be used as a context manager, and will yield 
     a recording object with two attributes: `output` and `records`. 
     At the end of the context manager, the `output` attribute will 
     be a list of the matching formatted log messages and the 
     `records` attribute will be a list of the corresponding LogRecord 
     objects. 

     Example:: 

      with self.assertLogs('foo', level='INFO') as cm: 
       logging.getLogger('foo').info('first message') 
       logging.getLogger('foo.bar').error('second message') 
      self.assertEqual(cm.output, ['INFO:foo:first message', 
             'ERROR:foo.bar:second message']) 
     """ 
     return _AssertLogsContext(self, logger, level) 

今すぐあなたのユニットテストモジュールで使用すると、そのクラスを使用することができます:

#test_my_module 
from logger_test import LogTestCase 

class TestMyModule(LogTestCase): 

    def test_some_feature(self): 
     with self.assertLogs('foo', level='INFO') as cm: 
      logging.getLogger('foo').info('first message') 
      logging.getLogger('foo.bar').error('second message') 
     self.assertEqual(cm.output, ['INFO:foo:first message', 
         'ERROR:foo.bar:second message']) 
そして、それはそうここにある、Python2.7に備えてポートをバックアップするために非常に簡単です
関連する問題