单元测试

 

经过系统的几个月测试,对测试有一定的了解,发现单元测试是测试一个非常重要的一部分,于是对单元测试发生了兴趣,通过阅读资料、实践。对单元测试进行了一些总结,希望能够对大家有些帮助,得到抛砖引玉的作用。<o:p></o:p>

1.     什么是单元测试

单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。例如,你可能把一个很大的值放入一个有序list 中去,然后确认该值出现在list 的尾部。或者,你可能会从字符串中删除匹配某种模式的字符,然后确认字符串确实不再包含这些字符了。<o:p></o:p>

2.     单元测试中的误区

系统开发中,我们有不少的测试误区,特别是对单元测试的误区,下面列出一些单元测试的误区,认识他们,了解他们,让我们系统开发中克服他们,让我们的程序变得更加稳定。<o:p></o:p>

1、  编写单元测试太花时间了<o:p></o:p>

在开发时越早发现BUG,就能节省更多的时间,降低更多的风险。如果不及时修改错误,一后期发现的错误,调试修改很困难,更浪费时间;二是维护越多,代码的结构越乱,甚至改变当初的实际思路。<o:p></o:p>

2、  运行测试的时间太长了<o:p></o:p>

合适的测试是不会让这种情况发生的。实际上,大多数测试的执行都是非常快的,因此你在几秒之内就可以运行成千上万个测试。但是有时某些测试会花费很长的时间。这时,需要把这些耗时的测试和其他测试分开。通常可以每天运行这种测试一次,或者几天一次。<o:p></o:p>

3、  测试代码并不是我的工作<o:p></o:p>

工作就是保证代码能够正确的完成,恰恰相反,测试代码是不可缺少的工作。<o:p></o:p>

4、  不清楚代码的行为,所以也就无从测试<o:p></o:p>

如果实在不清楚代码的行为,那么估计现在并不是编码的时候。如果并不知道代码的行为,那么你又如何知道你编写的代码是正确的呢?<o:p></o:p>

5、  但是这些代码都能够编译通过<o:p></o:p>

代码通过编译只是验证它的语法通过,但并不能保证它的行为就一定正确。<o:p></o:p>

6、 项目进度吃紧时少做些测试,时间富裕时多做测试<o:p></o:p>

这是不重视软件测试的表现,也是软件项目过程管理混乱的表现,必然会降低软件测试的质量。一个软件项目的顺利实现需要有合理的项目进度计划,其中包括合理的测试计划,对项目实施过程中的任何问题,都要有风险分析和相应的对策,不要因为开发进度的延期而简单的缩短测试时间、人力和资源。因为缩短测试时间带来的测试不完整,对项目质量的下降引起的潜在风险,往往造成更大的浪费。克服这种现象的最好办法是加强软件过程的计划和控制,包括软件测试计划、测试设计、测试执行、测试度量和测试控制。<o:p></o:p>

7、  软件测试是没有前途的工作,只有程序员才是软件高手<o:p></o:p>

      项目的成功往往靠个别全能程序员决定,他们负责总体设计和程序详细设计,认为软件开发就是编写代码,给人的印象往往是程序员是真正的牛人,具有很高的地位和待遇。因此,在这种环境下,软件测试很不受重视,软件测试人员的地位和待遇自然就很低了,甚至软件测试变得可有可无。随着市场对软件质量的不断提高,软件测试将变得越来越重要,相应的软件测试人员的地位和待遇将会逐渐提高。在微软等软件过程比较规范的大公司,软件测试人员的数量和待遇与程序员没有多大差别,优秀测试人员的待遇甚至比程序员还要高。软件测试将会成为一个具有很大发展前景的行业,软件测试大有前途,市场需要更多具有丰富测试技术和管理经验的测试人员,他们同样是软件专家。<o:p></o:p>

3.     为什么要使用单元测试

认识到测试中的误区,那为什么要进行单元测试呢?编写代码时,一定会反复调试保证它能够编译通过。如果是编译没有通过的代码,没有任何人会愿意交付给自己的老板。但代码通过编译,只是说明了它的语法正确;却无法保证它的语义也一定正确,没有任何人可以轻易承诺这段代码的行为一定是正确的。幸运,单元测试会为我们的承诺做保证。编写单元测试就是用来验证这段代码的行为是否与我们期望的一致。有了单元测试,我们可以自信的交付自己的代码,而没有任何的后顾之忧。<o:p></o:p>

4.     单元测试有哪些优点

1、它是一种验证行为。<o:p></o:p>

      程序中的每一项功能都是测试来验证它的正确性。它为以后的开发提供支缓。就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西。而且它为代码的重构提供了保障。这样,我们就可以更自由的对程序进行改进。<o:p></o:p>

2、它是一种设计行为。<o:p></o:p>

      编写单元测试将使我们从调用者观察、思考。特别是先写测试(test-first),迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合。<o:p></o:p>

3、它是一种编写文档的行为。<o:p></o:p>

      单元测试是一种无价的文档,它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的,并且它保持最新,永远与代码同步。<o:p></o:p>

4、它具有回归性。<o:p></o:p>

      自动化的单元测试避免了代码出现回归,编写完成之后,可以随时随地的快速运行测试。<o:p></o:p>

5.     什么时候进行单元测试

单元测试越早越好,早到什么程度?XP开发理论讲究TDD,即测试驱动开发,先编写测试代码,再进行开发。在实际的工作中,可以不必过分强调先什么后什么,重要的是高效和感觉舒适。从经验来看,先编写产品函数的框架,然后编写测试函数,针对产品函数的功能编写测试用例,然后编写产品函数的代码,每写一个功能点都运行测试,随时补充测试用例。所谓先编写产品函数的框架,是指先编写函数空的实现,有返回值的随便返回一个值,编译通过后再编写测试代码,这时,函数名、参数表、返回类型都应该确定下来了,所编写的测试代码以后需修改的可能性比较小。

6.     由谁来进行单元测试

单元测试与其他测试不同,单元测试可看作是编码工作的一部分,应该由程序员完成,也就是说,经过了单元测试的代码才是已完成的代码,提交产品代码时也要同时提交测试代码。测试部门可以作一定程度的审核。<o:p></o:p>

7.     单元测试的任务

单元测试任务包括:1 模块接口测试;2 模块局部数据结构测试;3 模块边界条件测试;4 模块中所有独立执行通路测试;5 模块的各条错误处理通路测试。

模块接口测试是单元测试的基础。只有在数据能正确流入、流出模块的前提下,其他测试才有意义。测试接口正确与否应该考虑下列因素:<o:p></o:p>

1 、输入的实际参数与形式参数的个数是否相同;<o:p></o:p>

2、 输入的实际参数与形式参数的属性是否匹配;  <o:p></o:p>

3 、输入的实际参数与形式参数的量纲是否一致;<o:p></o:p>

4、 调用其他模块时所给实际参数的个数是否与被调模块的形参个数相同;<o:p></o:p>

5、 调用其他模块时所给实际参数的属性是否与被调模块的形参属性匹配;<o:p></o:p>

6、调用其他模块时所给实际参数的量纲是否与被调模块的形参量纲一致;<o:p></o:p>

7、 调用预定义函数时所用参数的个数、属性和次序是否正确;<o:p></o:p>

8 、是否存在与当前入口点无关的参数引用;<o:p></o:p>

9、 是否修改了只读型参数;<o:p></o:p>

10 、对全程变量的定义各模块是否一致;<o:p></o:p>

11、是否把某些约束作为参数传递。<o:p></o:p>

如果模块内包括外部输入输出,还应该考虑下列因素:  1 、文件属性是否正确;<o:p></o:p>

2、 OPEN/CLOSE语句是否正确;<o:p></o:p>

3、 格式说明与输入输出语句是否匹配;<o:p></o:p>

4、缓冲区大小与记录长度是否匹配;<o:p></o:p>

5、文件使用前是否已经打开;<o:p></o:p>

6、是否处理了文件尾;<o:p></o:p>

7、是否处理了输入/输出错误;<o:p></o:p>

8、输出信息中是否有文字性错误;<o:p></o:p>

检查局部数据结构是为了保证临时存储在模块内的数据在程序执行过程中完整、正确。局部数据结构往往是错误的根源,应仔细设计测试用例,力求发现下面几类错误:<o:p></o:p>

1 、不合适或不相容的类型说明;<o:p></o:p>

2、变量无初值;<o:p></o:p>

3、变量初始化或省缺值有错;<o:p></o:p>

4、不正确的变量名(拼错或不正确地截断); <o:p></o:p>

5出现上溢、下溢和地址异常。<o:p></o:p>

除了局部数据结构外,如果可能,单元测试时还应该查清全局数据(例如FORTRAN的公用区)对模块的影响。<o:p></o:p>

在模块中应对每一条独立执行路径进行测试,单元测试的基本任务是保证模块中每条语句至少执行一次。此时设计测试用例是为了发现因错误计算、不正确的比较和不适当的控制流造成的错误。此时基本路径测试和循环测试是最常用且最有效的测试技术。计算中常见的错误包括:<o:p></o:p>

 1、 误解或用错了算符优先级;<o:p></o:p>

 2、混合类型运算;<o:p></o:p>

 3、变量初值错;<o:p></o:p>

 4、精度不够;<o:p></o:p>

 5、表达式符号错。<o:p></o:p>

比较判断与控制流常常紧密相关,测试用例还应致力于发现下列错误: <o:p></o:p>

1、不同数据类型的对象之间进行比较;<o:p></o:p>

2、错误地使用逻辑运算符或优先级;

3、因计算机表示的局限性,期望理论上相等而实际上不相等的两个量相等;

4、比较运算或变量出错;

5、循环终止条件或不可能出现;

6、迭代发散时不能退出;

7、错误地修改了循环变量。

一个好的设计应能预见各种出错条件,并预设各种出错处理通路,出错处理通路同样需要认真测试,测试应着重检查下列问题:

1、输出的出错信息难以理解;

2、记录的错误与实际遇到的错误不相符;

3、在程序自定义的出错处理段运行之前,系统已介入;

4、异常处理不当;

5、错误陈述中未能提供足够的定位出错信息。

     边界条件测试是单元测试中最后,也是最重要的一项任务。众的周知,软件经常在边界上失效,采用边界值分析技术,针对边界值及其左、右设计测试用例,很有可能发现新的错误。<o:p></o:p>

8.     单元测试用例及用例设计

了解了单元测试的任务,下面介绍测试用例,测试用例也是单元测试的核心,决定你的单元测试是否成功。测试用例的核心是输入数据。预期输出是依据输入数据和程序功能来确定的,也就是说,对于某一程序,输入数据确定了,预期输出也就可以确定了,至于生成/销毁被测试对象和运行测试的语句,是所有测试用例都大同小异的。<o:p></o:p>

单元测试测试用例一般采用逻辑覆盖法和基本路径法进行设计。<o:p></o:p>

8.1.   逻辑覆盖法

逻辑覆盖是以程序内部的逻辑结构为基础的测试用例设计技术,这一方法要求测试人员对程序的逻辑结构有清楚的了解。逻辑覆盖可分为:语句覆盖、判定覆盖、条件覆盖、判定-条件覆盖、条件组合覆盖与路径覆盖。<o:p></o:p>

1. 语句覆盖就是设计若干个测试用例,运行所测程序,使得每一可执行语句至少执行一次。<o:p></o:p>

2. 判定覆盖就是设计若干个测试用例,运行所测程序,使得程序中每个判断的取真分支和取假分支至少经历一次。<o:p></o:p>

3. 条件覆盖就是设计若干个测试用例,运行所测程序,使得程序中每个判断的每个条件的可能取值至少执行一次。<o:p></o:p>

4. 判定--条件覆盖就是设计足够的测试用例,使得判断中每个条件的所有可能取值至少执行一次,同时每个判断的所有可能判断结果也至少执行一次。<o:p></o:p>

5. 条件组合覆盖就是设计足够的测试用例,运行所测程序,使得每个判断的所有可能的条件取值组合至少执行一次。<o:p></o:p>

6. 路径测试就是设计足够的测试用例,覆盖程序中所有可能的路径。<o:p></o:p>

8.2.   基本路径法

基本路径测试法是在程序控制流图的基础上,通过分析控制构造的环路复杂性,导出基本可执行路径集合,从而设计测试用例的方法。设计出的测试用例要保证在测试中程序的每个可执行语句至少执行一次。基本路径测试法包括以下5个方面:<o:p></o:p>

1. 程序的控制流图:描述程序控制流的一种图示方法。<o:p></o:p>

2. 程序环境复杂性:McCabe复杂性度量;从程序的环路复杂性可导出程序基本路径集合中的独立路径条数,这是确定程序中每个可执行语句至少执行依次所必须的测试用例数目的上界。<o:p></o:p>

3. 导出测试用例。<o:p></o:p>

4. 准备测试用例,确保基本路径集中的每一条路径的执行。<o:p></o:p>

5. 图形矩阵:是在基本路径测试中起辅助作用的软件工具,利用它可以实现自动地确定一个基本路径集。<o:p></o:p>

8.3.   单元测试用例设计案例

好久前看过一篇非常不错的单元测试用例设计案例,但没有保留下来,感觉好可惜。一个好的单元测试案例对单元测试的入门是非常重要,希望这个案例能够帮助你对单元测试一些了解和对单元测试重要性的一个认识。<o:p></o:p>

例如币种换算单元函数,要对这个函数进行单元测试。应该怎么去做个函数的单元测试呢?首先考虑的是该币种换算单元函数的输入输出参数,其次是输出结果与期望值是否相等;再次建立单元测试处理类,并相应建立测试该函数的测试方法,按照JUnit规范一般是test****()。<o:p></o:p>

<span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family

相关推荐