单元测试

    技术2022-05-11  153

    单元测试

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

     

     

     

    1.     什么是单元测试

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

     

     

     

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

     

    编写代码时,一定会反复调试保证它能够编译通过。如果是编译没有通过的代码,没有任何人会愿意交付给自己的老板。但代码通过编译,只是说明了它的语法正确;却无法保证它的语义也一定正确,没有任何人可以轻易承诺这段代码的行为一定是正确的。

     

     

     

          幸运,单元测试会为我们的承诺做保证。编写单元测试就是用来验证这段代码的行为是否与我们期望的一致。有了单元测试,我们可以自信的交付自己的代码,而没有任何的后顾之忧。但我们对单元测试也有一些认识的误区,误区有哪些呢?

     

     

     

    1、  编写单元测试太花时间了

     

     

     

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

     

     

     

    2、  运行测试的时间太长了

     

     

     

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

     

     

     

    3、  测试代码并不是我的工作

     

     

     

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

     

     

     

    4、  不清楚代码的行为,所以也就无从测试

     

     

     

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

     

     

     

    5、  但是这些代码都能够编译通过

     

     

     

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

     

     

     

    6、 项目进度吃紧时少做些测试,时间富裕时多做测试

     

     

     

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

     

     

     

    7、  软件测试是没有前途的工作,只有程序员才是软件高手

     

     

     

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

     

     

     

    3.     单元测试有哪些优点

    1、它是一种验证行为。

     

     

     

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

     

     

     

    2、它是一种设计行为。

     

     

     

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

     

     

     

    3、它是一种编写文档的行为。

     

     

     

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

     

     

     

    4、它具有回归性。

     

     

     

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

     

     

     

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

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

     

     

     

    5.     由谁来进行单元测试

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

     

     

     

    6.     单元测试的任务

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

     

     

     

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

     

     

     

    1 、输入的实际参数与形式参数的个数是否相同;

     

     

     

    2 输入的实际参数与形式参数的属性是否匹配;  

     

     

     

    3 、输入的实际参数与形式参数的量纲是否一致;

     

     

     

    4 调用其他模块时所给实际参数的个数是否与被调模块的形参个数相同;

     

     

     

    5 调用其他模块时所给实际参数的属性是否与被调模块的形参属性匹配;

     

     

     

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

     

     

     

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

     

     

     

    8 、是否存在与当前入口点无关的参数引用;

     

     

     

    9 是否修改了只读型参数;

     

     

     

    10 、对全程变量的定义各模块是否一致;

     

     

     

    11、是否把某些约束作为参数传递。

     

     

     

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

     

     

     

      2 OPEN/CLOSE语句是否正确;

     

     

     

      3 格式说明与输入输出语句是否匹配;

     

     

     

      4、缓冲区大小与记录长度是否匹配;

     

     

     

      5、文件使用前是否已经打开;

     

     

     

      6、是否处理了文件尾;

     

     

     

      7、是否处理了输入/输出错误;

     

     

     

      8、输出信息中是否有文字性错误;

     

     

     

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

     

     

     

      1 、不合适或不相容的类型说明;

     

     

     

      2、变量无初值;

     

     

     

      3、变量初始化或省缺值有错;

     

     

     

      4、不正确的变量名(拼错或不正确地截断);

     

     

     

      5出现上溢、下溢和地址异常。

     

     

     

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

     

     

     

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

     

     

     

     1 误解或用错了算符优先级;

     

     

     

     2、混合类型运算;

     

     

     

     3、变量初值错;

     

     

     

     4、精度不够;

     

     

     

     5、表达式符号错。

     

     

     

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

     

     

     

      1、不同数据类型的对象之间进行比较;

     

     

     

      2、错误地使用逻辑运算符或优先级;  3、因计算机表示的局限性,期望理论上相等而实际上不相等的两个量相等;  4、比较运算或变量出错;  5、循环终止条件或不可能出现;  6、迭代发散时不能退出;  7、错误地修改了循环变量。  一个好的设计应能预见各种出错条件,并预设各种出错处理通路,出错处理通路同样需要认真测试,测试应着重检查下列问题:  1、输出的出错信息难以理解;  2、记录的错误与实际遇到的错误不相符;  3、在程序自定义的出错处理段运行之前,系统已介入;  4、异常处理不当;  5、错误陈述中未能提供足够的定位出错信息。     边界条件测试是单元测试中最后,也是最重要的一项任务。众的周知,软件经常在边界上失效,采用边界值分析技术,针对边界值及其左、右设计测试用例,很有可能发现新的错误。

     

     

     

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

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

     

     

     

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

     

     

     

    7.1.   逻辑覆盖法

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

     

     

     

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

     

     

     

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

     

     

     

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

     

     

     

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

     

     

     

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

     

     

     

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

     

     

     

    7.2.   基本路径法

     

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

     

     

     

    1. 程序的控制流图:描述程序控制流的一种图示方法。

     

     

     

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

     

     

     

    3. 导出测试用例。

     

     

     

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

     

     

     

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

     

     

     

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

     

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

     

     

     

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

     

     

     

    开始准备建立单元测试类及其测试方法,币种换算函数的输入参数为:目标币种,转换币种,金额,输出为转换后目标币种的金额。建立第一个简单的单元测试处理类及单元测试测试方法:

     

     

     

    Public BigDecimal testCurryChange(){

     

     

     

               引用币种换算函数并输入测试参数,假如测试参数为:目标币种为人民币,转换币种为美元,金额为100.00。

     

     

     

               BigDecimal bgResult = 799.95;

     

     

     

               If(bdResult.equals(函数(”10”,”32”,100.00== false){

     

     

     

                        Throw new Exception(“转换错误!”);

     

     

     

               }

     

     

     

    }

     

     

     

      第一个单元测试函数已经写完毕,可以进行单元测试,但不能完全测试币种换算函数是否完全正确,第一金额的不同转换结构可能不同;第二不同币种之间转换,结果不同。那下一步应该怎么做呢?

     

     

     

    可以采用这样的方法,建立同上的多个函数,但输入参数不一样,如金额不一样;目标币种与转换币种不一样。这样也可以在不同的方向来检测币种换算函数是否正确。一般情况下对于这种情况下,金额输入参数处理会才用边界发,取一个最小值和最大值作为输入参数进行检验,另外是看实际情况,要换算多少中币种,进行穷举法,把所有的币种进行处理,根据金额,币种的组合分别进行测试。这儿就不一一写出代码。

     

     

     

    但是上述方法虽然是一个可行的方法,但处理起来比较复杂,如果币种换算比较多,组合起来要写很多的单元测试方法,用起来比较烦琐,那应该怎么处理呢?在这种情况下可以采用数组的方法+循环的方法来实现,这样就很容易避开上述方法的烦琐。具体实现就不列举出来,大家可以自行研究一下。

     

     

     

    写单元测试也是一个非常有趣的事情,上述方法在不同的情况都有可能实现,不是每一个单元测试案例都要追求最完美,能够达到要求和实现目标即可。还有很多写测试案例的方法就不一一列举,大家可以根据实际情况而使用最好的方法。

     

     

     

    8.     单元测试工具介绍

    不同的开发语言,单元测试的工具也不一样。JAVA语言开发系统:目前比较流行的是JUnit.Net语言开发系统:目前比较流行的是:NUnitC/C++语言开发系统:目前比较流行的是CPPUnit。目前系统开发是用的JAVA,于是对JUnit研究的比较多,有机会可以和大家共同探讨一下,其他的如果有兴趣,大家也可以自行研究。还有很多的单元测试工具,可以根据具体情况而选择合适自己的单元测试工具。

     

     

     

      JUnit的简介:

     

     

     

             http://blog.csdn.net/askmyself/archive/2005/10/10/498599.aspx

     

     

     

            

     

     

     

      NUnit的简介:

     

     

     

             http://blog.csdn.net/litp/archive/2005/11/11/527684.aspx

     

     

     

           http://blog.csdn.net/litp/archive/2005/11/11/527684.aspx

     

     

     

     

     

     

      CPPUnit的简介:

     

     

     

             http://blog.csdn.net/lzw2005/archive/2005/09/23/488013.aspx

     

     

     

    http://blog.csdn.net/casualgame/archive/2005/03/27/332162.aspx

     

     

     

      

     

     

     

    通过单元测试的阅读和了解,希望能够把单元测试工具JUnit引入到目前的系统开发中来,而在实践过程中发现并不是十分可行,在进行测试的参数中并不能很好地获得一个数据库连接。于是通过别的方法来代替JUnit而达到单元测试,自己编写一个或多个单元测试业务处理类+Log4j(日志记录)也同样能够实现单元测试,相对来说这样比较麻烦,要自己写对象的创建释放、测试业务处理等等。在实际情况中,或者能够同样遇到这样的问题,希望大家能够在实践过程中找到自己合适的方法,而不是一定要采用单元测试工具。

     

     

     

    9.     总结

    总而言之,单元测试会让我们的开发工作变得更加轻松,让我们对自己的代码更加自信。无论是大型项目还是小型项目,无论是时间紧迫的项目还是时间宽裕的项目,只要代码不是一次写完永不改动,编写单元测试就一定超值,它已成为我们编码不可缺少的一部分。

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     


    最新回复(0)