單元測驗
什么是單元測驗, 維基百科上是這么定義的: unit testing is a method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine if they are fit for use.[1] Intuitively, one can view a unit as the smallest testable part of an application. 簡而言之,就是驗證系統中最小可測驗單元的功能是否正確的自動化測驗,因此,單元測驗的目地就是“對被測驗物件的職責進行驗證”, 在寫單元測驗之前,先識別出被測驗物件的職責,就知道該怎么寫這個單元測驗了,
根據被測驗物件,單元測驗可以分為兩大類:
- 對不依賴于外部資源的組件的單元測驗:使用unittest基本功能即可
- 對依賴于外部資源的組件的單元測驗:需要使用mock
unittest使用
python單元測驗庫unittest的基本使用參見廖雪峰Python單元測驗
具體使用參考以下資料
- Python中的單元測驗
- ningning.today-flask專案單元測驗實踐
- Python unittest官方檔案
- 極客學院-單元測驗
- nicholas-怎樣寫單元測驗
mock
為什么要用mock?

看了很多篇mock的講解,寫的最好的一篇是[Naftuli Kay-An Introduction to Mocking in Python,以洗掉檔案為例組成深入講解mock的使用,其他資料可以參見:
- Python單元測驗和Mock測驗
- 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/17 11: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
class TestQkLog(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 = https://www.cnblogs.com/CHLL55/p/True
log_init()
self.assertFalse(mock_logging.getLogger('debug').debug.called, "Failed to not write log.")
mock_caler.return_value = https://www.cnblogs.com/CHLL55/p/('qk_log_test', 12, '')
mock_datetime.now.return_value = https://www.cnblogs.com/CHLL55/p/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 = https://www.cnblogs.com/CHLL55/p/True
log_init()
self.assertFalse(mock_logging.getLogger('debug').info.called, "Failed to not write log.")
mock_caler.return_value = https://www.cnblogs.com/CHLL55/p/('qk_log_test', 12, '')
mock_datetime.now.return_value = https://www.cnblogs.com/CHLL55/p/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 = https://www.cnblogs.com/CHLL55/p/True
log_init()
self.assertFalse(mock_logging.getLogger('warn').info.called, "Failed to not write log.")
mock_caler.return_value = https://www.cnblogs.com/CHLL55/p/('qk_log_test', 12, '')
mock_datetime.now.return_value = https://www.cnblogs.com/CHLL55/p/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 = https://www.cnblogs.com/CHLL55/p/True
log_init()
self.assertFalse(mock_logging.getLogger('error').info.called, "Failed to not write log.")
mock_caler.return_value = https://www.cnblogs.com/CHLL55/p/('qk_log_test', 12, '')
mock_datetime.now.return_value = https://www.cnblogs.com/CHLL55/p/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資料統計分析專案時打算引進單元測驗,在寫完公共庫的單元測驗之后發現花費在單元測驗上的時間較多,而且公共庫不常改動,業務邏輯有比較混亂,因此團隊決定放棄單元測驗,對于以快速上線的初創公司和初創團隊的專案來說,可以不用急著寫單元測驗,因為在一切改動都可能發生的情況下,再代碼丟棄的時候對應的單元測驗也就被丟棄了,浪費了過多的人力,
因此,初創團隊不建議寫單元測驗,做好程式埋點和監控報警即可,
記得幫我點贊哦!
精心整理了計算機各個方向的從入門、進階、實戰的視頻課程和電子書,按照目錄合理分類,總能找到你需要的學習資料,還在等什么?快去關注下載吧!!!

念念不忘,必有回響,小伙伴們幫我點個贊吧,非常感謝,
我是職場亮哥,YY高級軟體工程師、四年作業經驗,拒絕咸魚爭當龍頭的斜杠程式員,
聽我說,進步多,程式人生一把梭
如果有幸能幫到你,請幫我點個【贊】,給個關注,如果能順帶評論給個鼓勵,將不勝感激,
職場亮哥文章串列:更多文章

本人所有文章、回答都與著作權保護平臺有合作,著作權歸職場亮哥所有,未經授權,轉載必究!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/234621.html
標籤:Python
上一篇:程式員必備技能
