单元测试框架的主要部件分析
如果没有测试框架怎么写测试?
很多人直接在被测文件中写main函数,Java和Python都可以这么做。
假设我们的函数是一个质数的判别函数,输入是一个比较小的整数,如果是质数就输出 true , 就输出false
def is_prime(n: int):
if n < 0:
n = -n
if n == 1:
return False
if n == 2:
return True
i = 2
while i <= n // i:
if n % i == 0:
return False
i += 1
else:
return True
测试代码
if __name__ == '__main__':
ret1 = is_prime(2)
if ret1 is True:
print("ok")
else:
print("failed")
ret2 = is_prime(10)
if ret2 is False:
print("ok")
else:
print("failed")
...
这样写测试用例有哪些问题呢?
- 到底有多少用例,不好统计,想起来的时候加了一个用例,但是计数忘了改
- 修改某个用例不好定位维护,如果这个main下面有100个测试用例...
- 模块中新增了一个函数,测试用例
- 重复代码很多,每个用例几乎都是 if else 的结构
- 集成到流水线的时候,这种代码运行是个问题
- 复杂的测试用例,前置,清理要怎么做?如何做到用例之间互相隔离
为了解决以上问题:
我们需要:
- 封装测试断言,这样就不用大量的 if else 结构
- 测试自动化——这个后面详细说一下
- 封装测试结果,这部分需要至少报告错误,成功,异常的用例数量
- 对测试流程抽象——第一步做什么,第二步做什么,这个过程是一致的,只是具体的步骤不同
大概理出一个数据流的框架如下

数据流.png
这个框架和一般的应用程序有什么不一样?
- 输入的东西差异。一般的应用输入的内容通常是用户的操作,指令,或者从控制台输入的一些字符。测试框架的输入一定是写好的测试用例代码
- 执行流程。一般的应用的执行流程,是从入口函数(main) 进入,然后逐层调用,像一颗不断分叉的大树;
测试框架的执行流程更像一条流水线,非常平稳地把每个用例按一定的次序执行完毕,然后输出结果 - 输出。一般的应用输出是响应,调哪个函数返回对应的结果就行。测试框架运作起来就是一组用例按序执行,或者特定的用例,输出的结果是一个断言结果,成功或失败
现在聊聊测试自动化的含义
测试自动化和自动化测试一般在中文语境里的区别——如果特意强调的话——前者强调过程的自动化,后者则是测试用例中的执行步骤——测试框架里的“测试自动化”指的是用例集中的用例被自动发现收集并自动执行,而用例里面的测试语句本身是语言级别的(对单测而言),所以测试自动化一般是指我们把用例发现加载,从文件到内存的类型反射,这些过程。
构建单元测试框架的主要技术
- 反射. 从文件到内存,需要把代码内的对象还原到解释器(python)可以理解的对象
- 模版模式
模版模式是一种设计模式,把一个工作流定义好接口,然后在子类中重载这些方法来把具体的工作细节特化。
对测试用例而言,一般性的工作流通常是所谓的AAA模式——arrange, action, assertion .
arrange 指的的测试前置条件的设定(还包含测试清理动作)
action 测试执行动作。
我们将会在封装基本的 setUp 和tearDown来做前置后置
并且完成两个断言:
assertEqual, assertNotEqual
测试结果封装。这个是比较简单的,主要是统计测试用例数量,失败数,成功数,异常数等等
测试用例和测试套件的建模。
大体上就是这些,看起来是很简单的。但是细节很多。本文主要的目的是制作一个极简的框架,内容包括以上内容。丰富的断言API我们只设计2个。
然后我们只设计一种测试入口模式
import myunittest
if __name__ == '__main__':
myunittest.main()