この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
少し細かいですが、「このソースコードは意図したとおりにログ出力するのか??」を単体テストレベルでちゃんと確認しておくことで、品質の向上に繋がります。
今回はtestfixturesのLogCapture
を試してみました。使い方はこちらのドキュメントを参考にしました。
※pytest
を使っている場合は、こちらのブログで紹介されているLogCaptureFixture
を使っても良いと思います。
セットアップ
- バージョン
$ python --version
Python 3.6.9
- ライブラリインストール
pip install pytest testfixtures
コード
ソースコード(src/main.py)
import json
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
def main(is_error):
logger.debug('Hello World.')
logger.info('Function called.')
if is_error:
logger.error('Error occurred.')
テストコード(test/test_main.py)
import pytest
import logging
from testfixtures import LogCapture
from src.main import main
@pytest.fixture(scope='function')
def mock_logger():
with LogCapture() as log:
yield log
class TestClass:
def test_logging(self, mock_logger):
main(is_error=False)
print(main)
テスト実行
$ python -m pytest test/test_main.py -s
...省略
collected 1 item
test/test_main.py::TestClass::test_logging <- unit/test_main.py
src.main DEBUG
Hello World.
src.main INFO
Function called.
PASSED
いろいろ試してみる
ログ出力のテスト
...省略
def test_logging_1(self, mock_logger):
main(is_error=False)
mock_logger.check(
(
'src.main',
'DEBUG',
'Hello World.'
),
(
'src.main',
'INFO',
'Function called.'
)
)
# 一旦クリア
mock_logger.clear()
main(is_error=True)
mock_logger.check(
(
'src.main',
'DEBUG',
'Hello World.'
),
(
'src.main',
'INFO',
'Function called.'
),
(
'src.main',
'ERROR',
'Error occurred.'
)
)
LogCaptureの有効化/無効化
...省略
def test_logging_2(self, mock_logger):
# 無効化
mock_logger.uninstall()
main(is_error=False)
mock_logger.check()
# 有効化
mock_logger.install()
main(is_error=False)
mock_logger.check(
(
'src.main',
'DEBUG',
'Hello World.'
),
(
'src.main',
'INFO',
'Function called.'
)
)
一部のログが含まれるかチェック
...省略
def test_logging_3(self, mock_logger):
main(is_error=False)
mock_logger.check_present(
(
'src.main',
'DEBUG',
'Hello World.'
)
)
順不同を許容してチェック
...省略
def test_logging_4(self, mock_logger):
main(is_error=False)
mock_logger.check_present(
(
'src.main',
'INFO',
'Function called.'
),
(
'src.main',
'DEBUG',
'Hello World.'
),
order_matters=False
)
ログレベルを指定してチェック
import pytest
import logging
from testfixtures import LogCapture
from src.main import main
@pytest.fixture(scope='function')
def mock_logger_only_error_level():
with LogCapture(level=logging.ERROR) as log:
yield log
class TestClass:
def test_logging(self, mock_logger_only_error_level):
main(is_error=True)
mock_logger_only_error_level.check(
(
'src.main',
'ERROR',
'Error occurred.'
)
)
log_capture
デコレータを使っても同じことができる
import pytest
import logging
from testfixtures import log_capture
from src.main import main
class TestClass:
@log_capture()
def test_logging(self, capture):
main(is_error=False)
capture.check_present(
(
'src.main',
'INFO',
'Function called.'
)
)
まとめ
いかがだったでしょうか。
個人の備忘録として書きましたが、どなたかの役に立てば幸いです。