单元测试怎么做的一些思考
1、是什么
单元测试(Unit Testing)又称为模块测试,是针对程序模块来进行正确性检验的测试工作。
- 程序模块是软件设计的最小单位,程序单元是应用的最小可测试部件
• 在面向过程编程中,一个单元就是单个程序、函数、过程等
• 在面向对象编程中,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法
2、测什么
并不是所有的项目都需要单元测试,一次性的项目不需要单元测试,持续迭代的产品需要单元测试,通常只有底层模块或者核心模块的测试中才会采用单元测试
并不是所有的“单元”都需要单元测试,服务代码、管理者代码、存储代码都应该是简单的顺序执行、没有逻辑的代码,不需要单元测试
同理,单元测试的代码不需要单元测试——单元测试的代码应该只包含简单的顺序执行、没有逻辑的代码
2.1 单元测试规则
必须进行测试的是通用、公共的Utils函数
复杂交互操作需要进行一定的测试
网络请求可以交给系统测试,或者不进行测试
2.2 可测试的代码
对于一个逻辑单元(方法),依赖于两个方面:一个是输入的参数,另一个是对外部的方法调用
只要确保输入参数不包含外部环境的上下文,同时内部代码对外部的调用也不包含环境上下文的访问,这个方法就是可单元测试的
2.3 判断可测试的代码
方法的运行是否需要mock
方法是否可以直接运行
方法的参数是否可以自由模拟,不依赖外部环境
2.4 改造代码
如果代码里有逻辑,但是不可单元测试的话,就需要改造代码
首先隔离逻辑代码和外部环境代码,再对逻辑代码进行单元测试
3、怎么测
3.1 单元测试的写法
通过提供预期的输入和预期的结果,与单元的实际运行结果进行对比,就可以知道单元的工作是否和预期的一致
各种输入参数的边界条件都需要测试到
单元测试的覆盖率可以帮助识别单元测试是否存在没有运行到的代码
3.2 命名方法
测试代码和业务代码的组织架构要保持一致,一一对应
单元测试的类名后加Test
单元测试的方法前加test
处于同一包名下,方便访问protected类型的方法
3.3 单元测试执行
构建输入参数,并预测该输入所产生的输出
调用要测试的目标方法,获取输出
Assert目标方法的输出是否和预期的输出一致
3.3.1 输入数据
被测试函数的输入参数
被测试函数内部需要读取的全局静态变量
被测试函数内部需要读取的成员变量
函数内部调用子函数获得的数据
函数内部调用子函数改写的数据
嵌入式系统中,在中断调用时改写的数据
3.3.2 预计输出
被测试函数的返回值
被测试函数的输出参数
被测试函数所改写的成员变量
被测试函数所改写的全局变量
被测试函数中进行的文件更新
被测试函数中进行的数据库更新
被测试函数中进行的消息队列更新
3.4 原则
覆盖分支和循环条件的所有路径
每个理想的测试案例独立于其它案例,即模块隔离
4、谁来测
单元测试更偏技术,它们通常关注代码细节,甚或是编程语言特有的概念,所以编写单元测试主要应该由研发负责
测试人员更了解如何制定测试计划,根据特定分析,如等价类划分和边界值分析,定义有价值的值。因此,程序员需要从测试人员那里“窃取”这类知识,
或者,他们可以和测试人员结对,一起讨论需要编写的测试,但是,之后应该由程序员实现它们
结对是最好的选择,因为测试人员和程序员可以更好地互相学习,拓展自己的知识面
5、何时测
开发过程中,单元测试应该来测试那些可能会出错的地方,或是那些边界情况
维护过程中,单元测试应该跟着QA的bug report来走,每一个bug都应该有个单元测试。
于是研发就会对自己的代码变更有两个自信,一是bug被fixed,二是相同的bug不会再次出现
6、怎么测
团队制度,单元测试是团队成熟度的基本标志
把单元测试执行、代码覆盖率统计和持续集成流水线做集成,以确保每次代码递交,都会自动触发单元测试,并在单元测试执行过程中自动统计代码覆盖率,
最后以“单元测试通过率”和“代码覆盖率”为标准来决定本次代码递交是否能够被接受。
7、局限
单元测试只测试程序单元自身的功能
单元测试只能表明测到的问题,不能表明不存在未测试到的错误
单元测试不能发现集成错误、性能问题、或者其他系统级别的问题,结合其他软件测试活动更为有效
研发可以从QA那里学习一些测试用例的基本概念。
单元测试还要考虑成本和收益。
软件的质量不是测试出来的,而是设计和维护出来的。