Pythonユニットテスト

単体テスト#

ユニットテストとは、Wikipediaで定義されています。ユニットテストは、ソースコードの個々のユニット、1つ以上のコンピュータプログラムモジュールのセット、および関連する制御データ、使用手順、および操作手順をテストする方法です。 [1]直感的には、ユニットをアプリケーションの最小のテスト可能な部分と見なすことができます。つまり、システム内の最小のテスト可能なユニットの機能が正しいかどうかを検証する自動テストです。したがって、ユニットテストの目的は「テスト対象の責任を確認する」ことです。ユニットテストを作成する前に、まずテスト対象の責任を特定してから、このユニットテストの作成方法を理解してください。

テスト対象に応じて、ユニットテストは2つのカテゴリに分類できます。

unittestの使用法#

pythonユニットテストライブラリunittestの基本的な使用法については、[Liao Xuefeng Pythonユニットテスト](https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143191629979802b566644aa84656b50cd484ec4a7838000)を参照してください。

特定の用途については、以下の情報を参照してください

  1. [ Pythonでのユニットテスト](https://pm.readthedocs.io/unittest/python.html)
  2. [ ningning.today-flaskプロジェクトユニットテストプラクティス](http://ningning.today/2016/11/22/python/flask-unittest/)
  3. [ Python unittestの公式ドキュメント](https://docs.python.org/2/library/unittest.html)
  4. [ オタクアカデミー-ユニットテスト](https://wiki.jikexueyuan.com/project/explore-python/Testing/README.html)
  5. [ ニコラス-ユニットテストの書き方](http://nicholas.ren/2012/10/21/my-understanding-on-unit-testing.html)

mock

なぜモックを使うのですか?

多くのモックの説明を読んだ後、最良の説明は[[Naftuli Kay-Pythonでのモックの概要](https://www.toptal.com/python/an-introduction-to-mocking-in-python)です。例としてファイルの削除を取り上げ、モックの使用法を詳しく説明します。その他の情報については、以下を参照してください。

  1. [ Pythonユニットテストとモックテスト](http://andrewliu.in/2015/12/12/Python%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E5% 92%8CMock%E6%B5%8B%E8%AF%95 /)
  2. mock-autospec

この記事に基づいてqk_logログモジュールを書き直してください。qk_log.pyコードは次のとおりです。

#! /usr/bin/env python
# - *- coding: utf-8-*-import os
import sys
import datetime
import logging
import logging.handlers

_ console_logger = None
_ warn_logger = None
_ error_logger = None

CONSOLE_FILENAME ='log/console.log'
WARNING_FILENAME ='log/warn.log'
ERROR_FILENAME ='log/error.log'

def log_init():if os.path.exists('log/') is True:
  pass
 else:
  os.mkdir('log/')
 global _console_logger, _warn_logger, _error_logger
 handler = logging.handlers.RotatingFileHandler(
  CONSOLE_FILENAME, maxBytes=20*1024*1024, backupCount=5)
 hdr = logging.StreamHandler()
 _ console_logger = logging.getLogger('debug')
 _ console_logger.addHandler(handler)
 _ console_logger.addHandler(hdr)
 _ console_logger.setLevel(logging.DEBUG)

 handler = logging.handlers.RotatingFileHandler(
  WARNING_FILENAME, maxBytes=20*1024*1024, backupCount=5)
 hdr = logging.StreamHandler()
 _ warn_logger = logging.getLogger('warn')
 _ warn_logger.addHandler(handler)
 _ warn_logger.addHandler(hdr)
 _ warn_logger.setLevel(logging.WARN)

 handler = logging.handlers.RotatingFileHandler(
  ERROR_FILENAME, maxBytes=20*1024*1024, backupCount=5)
 hdr = logging.StreamHandler()
 _ error_logger = logging.getLogger('error')
 _ error_logger.addHandler(handler)
 _ error_logger.addHandler(hdr)
 _ error_logger.setLevel(logging.ERROR)

def dlog(msg):
 file_name, file_no, unused =find_caler()
 time_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
 _ console_logger.debug('[%s] [%s] [%s,%d] %s'%(time_str,'debug', file_name, file_no, msg))

def ilog(msg):
 file_name, file_no, unused =find_caler()
 time_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
 _ console_logger.info('[%s] [%s] [%s,%d] %s'%(time_str,'info', file_name, file_no, msg))

def wlog(msg):
 file_name, file_no, unused =find_caler()
 time_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
 _ console_logger.warn('[%s] [%s] [%s,%d] %s'%(time_str,'warning', file_name, file_no, msg))
 _ warn_logger.warn('[%s] [%s] [%s,%d] %s'%(time_str,'warning', file_name, file_no, msg))

def elog(msg):
 file_name, file_no, unused =find_caler()
 time_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
 _ console_logger.error('[%s] [%s] [%s,%d] %s'%(time_str,'error', file_name, file_no, msg))
 _ error_logger.error('[%s] [%s] [%s,%d] %s'%(time_str,'error', file_name, file_no, msg))

def find_caler():
 f = sys._getframe(2)
 co = f.f_code
 return(os.path.basename(co.co_filename), f.f_lineno, co.co_name)if co != None else('unknown',0,'unknown')if __name__ =='__main__':log_init()dlog('test.log %d'%(123))ilog('test.log %d'%(123))wlog('test.log %d'%(123))elog('test.log %d'%(123))

ユニットテストコードは次のとおりです。

#! /usr/bin/env python
# - *- coding: utf-8-*-"""
Created on 10/10/1711:27 AM
@ author: Chen Liang
@ function:ログモジュールユニットテスト
"""

import sys

reload(sys)
sys.setdefaultencoding('utf-8')import unittest
import mock
import datetime
from qk_log import log_init, dlog, ilog, wlog, elog

classTestQkLog(unittest.TestCase):
 dt_str = datetime.datetime.strptime('2017-10-11 11:08:59','%Y-%m-%d %H:%M:%S')

 @ mock.patch('qk_log.os.path')
 @ mock.patch('qk_log.datetime.datetime')
 @ mock.patch('qk_log.logging')
 @ mock.patch('qk_log.find_caler')
 def test_dlog(self, mock_caler, mock_logging, mock_datetime, mock_path):
  mock_path.exists.return_value = True
  log_init()
  self.assertFalse(mock_logging.getLogger('debug').debug.called,"Failed to not write log.")

  mock_caler.return_value =('qk_log_test',12,'')
  mock_datetime.now.return_value = self.dt_str
  dlog('any msg')
  mock_logging.getLogger('debug').debug.assert_called_with('[%s] [%s] [%s,%d] %s'%('2017-10-11 11:08:59','debug','qk_log_test',12,'any msg'))

 @ mock.patch('qk_log.os.path')
 @ mock.patch('qk_log.datetime.datetime')
 @ mock.patch('qk_log.logging')
 @ mock.patch('qk_log.find_caler')
 def test_ilog(self, mock_caler, mock_logging, mock_datetime, mock_path):
  mock_path.exists.return_value = True
  log_init()
  self.assertFalse(mock_logging.getLogger('debug').info.called,"Failed to not write log.")
  mock_caler.return_value =('qk_log_test',12,'')
  mock_datetime.now.return_value = self.dt_str
  ilog('any msg')
  mock_logging.getLogger('debug').info.assert_called_with('[%s] [%s] [%s,%d] %s'%('2017-10-11 11:08:59','info','qk_log_test',12,'any msg'))

 @ mock.patch('qk_log.os.path')
 @ mock.patch('qk_log.datetime.datetime')
 @ mock.patch('qk_log.logging')
 @ mock.patch('qk_log.find_caler')
 def test_wlog(self, mock_caler, mock_logging, mock_datetime, mock_path):
  mock_path.exists.return_value = True
  log_init()
  self.assertFalse(mock_logging.getLogger('warn').info.called,"Failed to not write log.")
  mock_caler.return_value =('qk_log_test',12,'')
  mock_datetime.now.return_value = self.dt_str
  wlog('any msg')
  mock_logging.getLogger('warn').warn.assert_called_with('[%s] [%s] [%s,%d] %s'%('2017-10-11 11:08:59','warning','qk_log_test',12,'any msg'))

 @ mock.patch('qk_log.os.path')
 @ mock.patch('qk_log.datetime.datetime')
 @ mock.patch('qk_log.logging')
 @ mock.patch('qk_log.find_caler')
 def test_elog(self, mock_caler, mock_logging, mock_datetime, mock_path):
  mock_path.exists.return_value = True
  log_init()
  self.assertFalse(mock_logging.getLogger('error').info.called,"Failed to not write log.")
  mock_caler.return_value =('qk_log_test',12,'')
  mock_datetime.now.return_value = self.dt_str
  elog('any msg')
  mock_logging.getLogger('error').error.assert_called_with('[%s] [%s] [%s,%d] %s'%('2017-10-11 11:08:59','error','qk_log_test',12,'any msg'))if __name__ =='__main__':
 unittest.main()

ユニットテストに関する見解#

Pythonデータ統計分析プロジェクトの全体的な変革の中でユニットテストを導入する予定でした。パブリックライブラリのユニットテストを作成した後、ユニットテストに多くの時間が費やされ、パブリックライブラリは頻繁に変更されず、ビジネスロジックがかなり混沌としていたため、チームはユニットテストを中止することを決定しました。すぐにオンラインになるスタートアップやスタートアップチームのプロジェクトの場合、ユニットテストを急いで作成する必要はありません。すべての変更が可能な場合、コードが破棄されるときに対応するユニットテストも破棄されるため、無駄です。人員が多すぎる。

したがって、スタートアップチームは、ユニットテストを作成することをお勧めしません。ポイントを埋め、アラームを監視するだけです。

Recommended Posts

Pythonユニットテスト
Python CookBook
Python FAQ
Python3モジュール
python(you-get)
Python文字列
Pythonの基本
Pythonの基本2
Python exec
Pythonノート
Python3タプル
CentOS + Python3.6 +
Python Advanced(1)
Pythonデコレータ
Pythonマルチスレッド
Pythonツールチェーン
Python3リスト
Pythonマルチタスク-日常
Pythonの概要
pythonの紹介
Pythonアナリティック
Pythonの基本
07.Python3関数
Pythonの基本3
Pythonマルチタスクスレッド
Python関数
python sys.stdout
python演算子
Pythonエントリ-3
Centos 7.5 python3.6
Python文字列
pythonキューキュー
Pythonの基本4
Pythonの基本5