随着Refactoring技术和XP软件工程技术的广泛推广,单元测试的作用在软件工程中变得越来越重要,而一个简明易学、适用广泛、高效稳定的单元测试框架则对成功的实施单元测试有着至关重要的作用。在java编程语句环境里,Junit Framework是一个已经被多数java程序员采用和实证的优秀的测试框架,但是多数没有尝试Junit Framework的程序员在学习如何Junit Framework来编写适应自己开发项目的单元测试时,依然觉得有一定的难度,这可能是因为Junit随框架代码和实用工具附带的用户指南和文档的着重点在于解释单元测试框架的设计方法以及简单的类使用说明,而对在特定的测试框架(Junit)下如何实施单元测试,如何在项目开发的过程中更新和维护已经存在的单元测试代码没有详细的解释。因此本文档就两个着重点对Junit所附带的文档进行进一步的补充和说明,使Junit能被更多的开发团队采用,让单元测试乃至Refactoring、XP技术更好在更多的开发团队中推广。
1. 单元测试的编写原则
Junit附带文档所列举的单元测试带有一定的迷惑性,因为几乎所有的示例单元都是针对某个对象的某个方法,似乎Junit的单元测试仅适用于类组织结构的静态约束,从而使初学者怀疑Junit下的单元测试所能带来的效果。因此我们需要重新定义如何确定有价值的单元测试以及如何编写这些单元测试、维护这些单元测试,从而让更多的程序员接受和熟悉Junit下的单元测试的编写。
在Junit单元测试框架的设计时,作者一共设定了三个总体目标,第一个是简化测试的编写,这种简化包括测试框架的学习和实际测试单元的编写;第二个是使测试单元保持持久性;第三个则是可以利用既有的测试来编写相关的测试。从这三个目标可以看出,单元测试框架的基本设计考虑依然是从我们现有的测试方式和方法出发,而只是使测试变得更加容易实施和扩展并保持持久性。因此编写单元测试的原则可以从我们通常使用的测试方法借鉴和利用。
2. 如何确定单元测试
在我们通常的测试中,一个单元测试一般针对于特定对象的一个特定特性,譬如,假定我们编写了一个针对特定数据库访问的连接池的类包实现,我们会建立以下的单元测试:
在连接池启动后,是否根据定义的规则在池中建立了相应数量的数据库连接
申请一个数据库连接,是否根据定义的规则从池中直接获得缓存连接的引用,还是建立新的连接
释放一个数据库连接后,连接是否根据定义的规则被池释放或者缓存以便以后使用
后台Housekeeping线程是否按照定义的规则释放已经过期的连接申请
如果连接有时间期限,后台Housekeeping线程是否定期释放已经过期的缓存连接
这儿只列出了部分的可能测试,但是从这个列表我们可以看出单元测试的粒度。一个单元测试基本是以一个对象的明确特性为基础,单元测试的过程应该限定在一个明确的线程范围内。根据上面所述,一个单元测试的测试过程非常类似于一个Use Case的定义,但是单元测试的粒度一般来说比Use Case的定义要小,这点是容易理解的,因为Use Case是以单独的事务单元为基础的,而单元测试是以一组聚合性很强的对象的特定特征为基础的,一般而言一个事务中会利用许多的系统特征来完成具体的软件需求。
从上面的分析我们可以得出,测试单元应该以一个对象的内部状态的转换为基本编写单元。一个软件系统就和一辆设计好的汽车一样,系统的状态是由同一时刻时系统内部的各个分立的部件的状态决定的,因此为了确定一个系统最终的行为符合我们起始的要求,我们首先需要保证系统内的各个部分的状态会符合我们的设计要求,所以我们的测试单元的重点应该放在确定对象的状态变换上。
然而需要注意的并不是所有的对象组特征都需要被编写成独立的测试单元,如何在对象组特征里筛选有价值的测试单元的原则在JUnitTest Infected: Programmers Love Writing Tests一文中得到了正确的描述,你应该在有可能引入错误的地方引入测试单元,通常这些地方存在于有特定边界条件、复杂算法以及需求变动比较频繁的代码逻辑中。除了这些特性需要被编写成独立的测试单元外,还有一些边界条件比较复杂的对象方法也应该被编写成独立的测试单元,这部分单元测试已经在Junit文档中被较好的描述和解释过了。
文章来源于领测软件测试网 https://www.ltesting.net/