因为最近项目处于刚开展阶段,所以需要写的项目公共函数比较多。既然是公共函数,那么必然决定了它的属性是相对比较独立、功能比较单一的,这种函数使用doctest模块来保证质量再好不过了。并且doctest还有一个更大的好处就是可以清晰的把可能出现的使用场景以文档的形式保存在注释中,如果后面因为需求变更修改代码的话,只需要重新执行一遍doctest即可确保变更不会影响先有功能。好了,闲话少说,上代码。
def check_license_number(license_number):
"""
>>> check_license_number("粤B1234港")
True
>>> check_license_number("粤B123456")
True
>>> check_license_number("粤B123ddr")
False
>>> check_license_number("粤B123dd")
True
>>> check_license_number("粤B12345")
True
:param license_number:
:return: True or False
"""
if not license_number:
return False
max_chat_number = 0
for chat in license_number:
if chat in string.ascii_letters:
max_chat_number += 1
if max_chat_number > CONST.LICENSE_NUMBER_MAX_CHAT:
return False
if len(set(license_number).intersection(string.ascii_letters)) > CONST.LICENSE_NUMBER_MAX_CHAT: # 车牌最多包含三个字母
return False
if license_number[-1:] in CONST.LICENSE_NUMBER_LAST_STRING_LIST: # ['港', '澳', '学', '领']
source_string = set(license_number[1:][:-1])
else:
source_string = set(license_number[1:])
check_string = string.ascii_letters + string.digits
if len(set(source_string).difference(check_string)) > 0: # 除字母、数字、首尾两个汉字外不能包含特殊字符
return False
else:
return True
代码是一段检验车牌有效性的函数,写好之后上网找了一些doctest的用法,发现主要是两种:
- 在代码文件中执行测试:if __name__ == '__main__': import doctest doctest.testmod(verbose=True)
- 在命令行中执行测试:python -m doctest -v utils.py
这两种测试执行方法都有一个共同点,就是都是对整个py文件的测试代码进行执行。因为我的代码属于项目中的模块,所以会调用一些公共的配置参数,这个时候显然是无法进行单文件执行的。那么上面两种方法是不能满足需求了,于是打开doctest.testmod函数的源码:
def testmod(m=None, name=None, globs=None, verbose=None,
report=True, optionflags=0, extraglobs=None,
raise_on_error=False, exclude_empty=False):
"""m=None, name=None, globs=None, verbose=None, report=True,
optionflags=0, extraglobs=None, raise_on_error=False,
exclude_empty=False
Test examples in docstrings in functions and classes reachable
from module m (or the current module if m is not supplied), starting
with m.__doc__.
Also test examples reachable from dict m.__test__ if it exists and is
not None. m.__test__ maps names to functions, classes and strings;
function and class docstrings are tested even if the name is private;
strings are tested directly, as if they were docstrings.
Return (#failures, #tests).
"""
pass
发现他的注释中有说明:
- 参数“m”代表从哪个模块开始,没有的话默认当前模块
- 参数“name”代表函数名,没有的话全文执行
有这两条的话基本就能满足我的需求了,然后打开Pycharm的Python Clonsole交互式命令行开始执行测试
>>> from common import utils
>>> import doctest
>>> doctest.testmod(utils, "check_license_number", verbose=True)
Trying:
check_license_number("粤B1234港")
Expecting:
True
ok
Trying:
check_license_number("粤B123456")
Expecting:
True
ok
Trying:
check_license_number("粤B123ddr")
Expecting:
False
ok
Trying:
check_license_number("粤B123dd")
Expecting:
True
ok
Trying:
check_license_number("粤B123")
Expecting:
True
ok
20 items had no tests:
check_license_number
check_license_number.ImageFileDispose
check_license_number.ImageFileDispose.__init__
check_license_number.ImageFileDispose.base64_to_image
check_license_number.ImageFileDispose.image_to_base64
check_license_number.ImageFileDispose.save_img
check_license_number.OSSFileSDK
check_license_number.OSSFileSDK.__init__
check_license_number.OSSFileSDK.download_file
check_license_number.OSSFileSDK.get_flow_file_postfix
check_license_number.OSSFileSDK.get_local_file_postfix
check_license_number.OSSFileSDK.get_new_file_name
check_license_number.OSSFileSDK.get_sign_url
check_license_number.OSSFileSDK.upload_file_bytes_file
check_license_number.OSSFileSDK.upload_file_local_file
check_license_number.OSSFileSDK.upload_file_network_flow
check_license_number.check_mobile_phone
check_license_number.reset_dup_wxuser_phone
check_license_number.timestamp_format
check_license_number.wechat_cookie
1 items passed all tests:
5 tests in check_license_number.check_license_number
5 tests in 21 items.
5 passed and 0 failed.
Test passed.
TestResults(failed=0, attempted=5)
到这里,整个测试就执行成功了,后面可以随便折腾函数的功能而不用担心对函数功能有什么影响了。