什麼是單元測試, 維基百科上是這麼定義的: 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. 簡而言之,就是驗證系統中最小可測試單元的功能是否正確的自動化測試。所以,單元測試的目地就是「對被測試對象的職責進行驗證」, 在寫單元測試以前,先識別出被測試對象的職責,就知道該怎麼寫這個單元測試了。html
根據被測試對象,單元測試能夠分爲兩大類:python
python單元測試庫unittest的基本使用參見廖雪峯Python單元測試程序員
具體使用參考如下資料flask
爲何要用mock?app
看了不少篇mock的講解,寫的最好的一篇是[Naftuli Kay-An Introduction to Mocking in Python,以刪除文件爲例組成深刻講解mock的使用。其餘資料能夠參見:ide
仿照這篇文章改寫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 = 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數據統計分析項目時打算引進單元測試,在寫完公共庫的單元測試以後發現花費在單元測試上的時間較多,並且公共庫不常改動,業務邏輯有比較混亂,所以團隊決定放棄單元測試。對於以快速上線的初創公司和初創團隊的項目來講,能夠不用急着寫單元測試,由於在一切改動均可能發生的狀況下,再代碼丟棄的時候對應的單元測試也就被丟棄了,浪費了過多的人力。測試
所以,初創團隊不建議寫單元測試,作好程序埋點和監控報警便可。ui
記得幫我點贊哦!
精心整理了計算機各個方向的從入門、進階、實戰的視頻課程和電子書,按照目錄合理分類,總能找到你須要的學習資料,還在等什麼?快去關注下載吧!!!
念念不忘,必有迴響,小夥伴們幫我點個贊吧,很是感謝。
我是職場亮哥,YY高級軟件工程師、四年工做經驗,拒絕鹹魚爭當龍頭的斜槓程序員。
聽我說,進步多,程序人生一把梭
若是有幸能幫到你,請幫我點個【贊】,給個關注,若是能順帶評論給個鼓勵,將不勝感激。
職場亮哥文章列表:更多文章
本人全部文章、回答都與版權保護平臺有合做,著做權歸職場亮哥全部,未經受權,轉載必究!