软件可测试性及常用Python测试技巧小结
问题
-
什么是代码的可测试性
-
如何写出可测试的代码?
-
有哪些常见的不好测试的代码?
写出可测试的代码
-
依赖注入
将对象的创建反转给上层逻辑,在外部创建好对象后再注入到下层代码中。
-
mock
若代码中依赖了外部系统或者不可控组件,比如,需要依赖数据库、网络通信、文件系统等,可以通过二次封装将被测代码与外部系统解依赖,提高可测试性.
比如用户类包含过期日期属性,假定我们需要对用户登录场景进行测试,其中一个判断逻辑是:如果用户过期,则返回登录失败。假如说_expire_data属性是经过封装的,未暴露对外接口进行修改,那么当测试过期用户登录时,可以对用户过期判定逻辑进行二次封装。
class User: id: int name: str _password: str _expire_date: datetime.datetime def is_expired()->bool: pass
def user_is_expired(user: User): return user.is_expired()
测试时可以对user_is_expired的返回值进行mock
def user_is_expired(user: User): return true
可能的问题
-
对外部服务的依赖
-
网络通信耗时
-
不可控因素
反模式
-
未决行为
代码输出是随机的,比如跟时间、随机数相关的代码。
-
滥用全局变量
-
滥用静态方法
-
复杂继承
-
高耦合代码
如何度量软件架构质量属性?
软件可测试性
软件测试的不同方面
-
功能
-
性能
常用的软件测试策略
- 存根
单元测试
测试一段代码:
# solution.py
from algorithms.base import BaseSolution
from utils import timer
class Solution(BaseSolution):
@timer
def run(self, nums1: list, m: int, nums2: list, n: int) -> list:
nums1[m:] = nums2
nums1.sort()
return nums1
class Solution1(BaseSolution):
@timer
def run(self, nums1: list, m: int, nums2: list, n: int) -> list:
array = []
s1_index, s2_index = 0, 0
while s1_index<m or s2_index<n:
if s1_index == m:
array.append(nums2[s2_index])
s2_index += 1
elif s2_index == n:
array.append(nums1[s1_index])
s1_index += 1
elif nums1[s1_index]<nums2[s2_index]:
array.append(nums1[s1_index])
s1_index += 1
else:
array.append(nums2[s2_index])
s2_index += 1
return array
solutions = [
Solution,
Solution1
]
通过单元测试框架针对所测模块执行测试用例
# test.py
import unittest
from algorithms.leetcode.alg88 import solution, case
from copy import deepcopy
class MyTestCase(unittest.TestCase):
def test_algorithms(self):
_solution = solution.solutions
for solution_i in _solution:
print("===", solution_i.__name__)
for _case in case.cases:
case_copy = deepcopy(_case)
print(case_copy)
result = solution_i().run(*case_copy['input'])
print(result)
self.assertEqual(result, case_copy['output'])
if __name__ == '__main__':
unittest.main()
代码覆盖率
内联测试
文档内联测试(doctest):无需开发或维护单独的测试套件,只需要在函数、类或者模块中组合代码测试即可。示例:
def is_anagram(word: str, other: str)->bool:
"""判断两个单词是否互为易位词
>>> is_anagram("stop", "pots")
True
>>> is_anagram("asda", "sa")
False
>>> is_anagram("stop", "otps")
True
"""
if len(word) != len(other):
return False
return sorted(list(word))==sorted(list(other))
测试自动化
测试驱动开发(TDD)
参考资料
-
《软件架构Python语言实现》-可测试性——编写可测试的代码(P58-P90)