Python unit testing

unit test#

What is unit testing, is defined on Wikipedia: 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. In short, it is an automated test that verifies whether the function of the smallest testable unit in the system is correct. Therefore, the purpose of unit testing is to "verify the responsibilities of the tested object". Before writing the unit test, first identify the responsibilities of the tested object and know how to write the unit test.

According to the tested object, unit tests can be divided into two categories:

unittest usage#

For the basic use of the python unit test library unittest, see Liao Xuefeng Python Unit Test

Refer to the following information for specific use

  1. Unit Testing in Python
  2. ningning.today-flask project unit test practice
  3. Python unittest official documentation
  4. Geek Academy-Unit Testing
  5. nicholas-how to write unit tests

mock

Why use mock?

After reading many mock explanations, the best one is written [Naftuli Kay-An Introduction to Mocking in Python, taking deleting files as an example to explain the use of mocks in depth. For other information, please refer to:

  1. [ Python unit test and Mock test](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

Rewrite the qk_log log module based on this article, the qk_log.py code is as follows

#! /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))

The unit test code is as follows:

#! /usr/bin/env python
# - *- coding: utf-8-*-"""
Created on 10/10/1711:27 AM
@ author: Chen Liang
@ function:Log module unit test
"""

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()

Views on unit testing#

I planned to introduce unit testing during an overall transformation of the Python data statistical analysis project. After writing the unit test of the public library, I found that more time was spent on unit testing, and the public library was not changed frequently, and the business logic was rather chaotic, so the team Decided to abandon unit testing. For projects of startups and startup teams that go online quickly, you don’t need to rush to write unit tests, because when all changes are possible, the corresponding unit tests are also discarded when the code is discarded, which is a waste. Too much manpower.

Therefore, the start-up team does not recommend writing unit tests, just burying points in the program and monitoring alarms.

Recommended Posts

Python unit testing
Python CookBook
Python FAQ
Python3 module
python (you-get)
Python string
Python basics
Python basics 2
Python exec
Python notes
Python3 tuple
CentOS + Python3.6+
Python advanced (1)
Python decorator
Python multithreading
Python toolchain
Python3 list
Python multitasking-coroutine
Python overview
python introduction
Python analytic
Python basics
07. Python3 functions
Python basics 3
Python multitasking-threads
Python functions
python sys.stdout
python operator
Python entry-3
Centos 7.5 python3.6
Python string
python queue Queue
Python basics 4
Python basics 5