コード内の特定の条件により、ログメッセージがDjangoログに書き込まれるようにしたい。Djangoユニットでこれを行うにはどうすればよいですか。テストフレームワーク?
送信メールを確認するのと同じように、ログに記録されたメッセージを確認できる場所はありますか?私のユニットテストはDjango.test.TestCase
。
mock
モジュールを使用して、ロギングモジュールまたはロガーオブジェクトをモックします。それが終わったら、ロギング関数が呼び出される引数を確認してください。
たとえば、コードが次のようになっている場合:
import logging
logger = logging.getLogger('my_logger')
logger.error("Your log message here")
次のようになります。
from unittest.mock import patch # For python 2.x use from mock import patch
@patch('this.is.my.module.logger')
def test_check_logging_message(self, mock_logger):
mock_logger.error.assert_called_with("Your log message here")
Django.test.TestCase
からassertLogs
を使用することもできます
あなたがコーディングするとき
import logging
logger = logging.getLogger('my_logger')
def code_that_throws_error_log():
logger.error("Your log message here")
これはテストコードです。
with self.assertLogs(logger='my_logger', level='ERROR') as cm:
code_that_throws_error_log()
self.assertIn(
"ERROR:your.module:Your log message here",
cm.output
)
これにより、ログのみのパッチ適用を回避できます。
ロガーオブジェクトをモックアウトする一般的な方法(素晴らしい章のSimeon Visserの回答を参照)は、実行したすべての場所でログをモックアウトするテストが必要になるという点で少し注意が必要です。これは、ロギングが複数のモジュールからのものである場合、または所有していないコード内にある場合は厄介です。ロギングの名前が変更されたモジュールの場合、テストが失敗します。
素晴らしい ' testfixtures 'パッケージには、生成されたすべてのログメッセージを、それらがどこから来たかに関係なくキャプチャするロギングハンドラーを追加するツールが含まれています。キャプチャされたメッセージは、後でテストによって問い合わせることができます。最も単純な形式:
次のログを記録するテスト対象のコードを想定します。
_import logging
logger = logging.getLogger()
logger.info('a message')
logger.error('an error')
_
これのテストは次のようになります。
_from testfixtures import LogCapture
with LogCapture() as l:
call_code_under_test()
l.check(
('root', 'INFO', 'a message'),
('root', 'ERROR', 'an error'),
)
_
「root」という単語は、ロギングがlogging.getLogger()
を使用して作成されたロガーを介して送信されたことを示します(つまり、引数なし)。getLoggerに引数を渡すと(___name__
_は従来型)、その引数は次のようになります。 'root'の代わりに使用されます。
テストでは、どのモジュールがロギングを作成したかは関係ありません。これは、サードパーティのコードを含む、テスト対象のコードによって呼び出されるサブモジュールである可能性があります。
渡された引数についてアサートするモックの手法とは対照的に、テストは生成された実際のログメッセージについてアサートします。これらは、logging.info呼び出しが、自分で展開しない追加の引数を含む '%s'形式の文字列を使用する場合は異なります(たとえば、logging.info('total=%s', len(items))
の代わりにlogging.info('total=%s' % len(items))
を使用する必要があります。これは余分な作業ではなく、「Sentry」などの架空の将来のロギング集約サービスが適切に機能することを可能にします。「total = 12」と「total = 43」が同じログメッセージの2つのインスタンスであることがわかります。これが、pylintの理由です。後者の形式の_logging.info
_呼び出しについて警告します。)
LogCaptureには、ログフィルタリングなどの機能が含まれています。別の素晴らしい章であるChrisWithersによって書かれたその親の「testfixtures」パッケージには、他の多くの便利なテストツールが含まれています。ドキュメントはここにあります: http://pythonhosted.org/testfixtures/logging.html
Djangoには、patch_logger
と呼ばれるNiceコンテキストマネージャー関数があります。
from Django.test.utils import patch_logger
次に、テストケースで:
with patch_logger('logger_name', 'error') as cm:
self.assertIn("Error message", cm)
どこ:
logger_name
はロガー名です(duh)error
はログレベルですcm
は、すべてのログメッセージのリストです詳細:
https://github.com/Django/django/blob/2.1/Django/test/utils.py#L638
Django <2.0でも、pythonバージョン(djでサポートされている限り)とは関係なく)同じように機能するはずです。