想飞就别怕摔

大爷的并TM骂人

JUnit、TDD 简单介绍(剪切优秀的文章)

一、一些概念性问题:

白盒测试——把测试对象看作一个打开的盒子,程序内部的逻辑结构和其他信息对测试人员是公开的。
回归测试——软件或环境的修复或更正后的“再测试”,自动测试工具对这类测试尤其有用。
单元测试——是最小粒度的测试,以测试某个功能或代码块。一般由程序员来做,因为它需要知道内部程序设计和编码的细节。

二、单元测试的好处 :

A、提高开发速度——测试是以自动化方式执行的,提升了测试代码的执行效率。
B、提高软件代码质量——它使用小版本发布至集成,便于实现人员除错。同时引入重构概念,让代码更干净和富有弹性。
C、提升系统的可信赖度——它是回归测试的一种。支持修复或更正后的“再测试”,可确保代码的正确性。

三、单元测试的针对对象 :

A、面向过程的软件开发针对过程。
B、面向对象的软件开发针对对象。
C、可以做类测试,功能测试,接口测试(最常用于测试类中的方法)。

四、下面是JUnit一些特性的总结:
 
1) 提供的API可以让你写出测试结果明确的可重用单元测试用例
2) 提供了三种方式来显示你的测试结果,而且还可以扩展
3) 提供了单元测试用例成批运行的功能
4) 超轻量级而且使用简单,没有商业性的欺骗和无用的向导
5) 整个框架设计良好,易扩展,对不同性质的被测对象,如Class,Jsp,Servlet,Ejb等,Junit有不同的使用技巧。
6) 使用断言方法判断期望值和实际值差异,返回Boolean值。
7) 测试驱动设备使用共同的初始化变量或者实例。
8) 测试包结构便于组织和集成运行。
9) 支持图型交互模式和文本交互模式。

五、JUnit的好处和JUnit单元测试编写原则:

好处: A、可以使测试代码与产品代码分开。 
              B、针对某一个类的测试代码通过较少的改动便可以应用于另一个类的测试。 
              C、易于集成到测试人员的构建过程中,JUnit和Ant的结合可以实施增量开发。 
              D、JUnit是公开源代码的,可以进行二次开发。 
              E、可以方便地对JUnit进行扩展。
编写原则:
              A、是简化测试的编写,这种简化包括测试框架的学习和实际测试单元的编写。 
              B、是使测试单元保持持久性。 
              C、是可以利用既有的测试来编写相关的测试。

六、JUnit框架组成 :

A、对测试目标进行测试的方法与过程集合,可称为测试用例(TestCase)。
B、测试用例的集合,可容纳多个测试用例(TestCase),将其称作测试包(TestSuite)。
C、测试结果的描述与记录。(TestResult) 。
D、测试过程中的事件监听者(TestListener)。
E、每一个测试方法所发生的与预期不一致状况的描述,称其测试失败元素(TestFailure)
F、JUnit Framework中的出错异常(AssertionFailedError)。

JUnit框架是一个典型的Composite模式:TestSuite可以容纳任何派生自Test的对象;当调用TestSuite对象的run()方法是,会遍历自己容纳的对象,逐个调用它们的run()方法

七、JUnit中常用的接口和类 :

1)Test接口——运行测试和收集测试结果
Test接口使用了Composite设计模式,是单独测试用例 (TestCase),聚合测试模式(TestSuite)及测试扩展(TestDecorator)的共同接口。
它的public int countTestCases()方法,它来统计这次测试有多少个TestCase,另外一个方法就是public void run( TestResult ),TestResult是实例接受测试结果, run方法执行本次测试。

2)TestCase抽象类——定义测试中固定方法
TestCase是Test接口的抽象实现,(不能被实例化,只能被继承)其构造函数TestCase(string name)根据输入的测试名称name创建一个测试实例。由于每一个TestCase在创建时都要有一个名称,若某测试失败了,便可识别出是哪个测试失败。
TestCase类中包含的setUp()、tearDown()方法。setUp()方法集中初始化测试所需的所有变量和实例,并且在依次调用测试类中的每个测试方法之前再次执行setUp()方法。tearDown()方法则是在每个测试方法之后,释放测试程序方法中引用的变量和实例。
开发人员编写测试用例时,只需继承TestCase,来完成run方法即可,然后JUnit获得测试用例,执行它的run方法,把测试结果记录在TestResult之中。

3)Assert静态类——一系列断言方法的集合
Assert包含了一组静态的测试方法,用于期望值和实际值比对是否正确,即测试失败,Assert类就会抛出一个AssertionFailedError异常,JUnit测试框架将这种错误归入Failes并加以记录,同时标志为未通过测试。如果该类方法中指定一个String类型的传参则该参数将被做为AssertionFailedError异常的标识信息,告诉测试人员改异常的详细信息。
JUnit 提供了6大类31组断言方法,包括基础断言、数字断言、字符断言、布尔断言、对象断言。
其中assertEquals(Object expcted,Object actual)内部逻辑判断使用equals()方法,这表明断言两个实例的内部哈希值是否相等时,最好使用该方法对相应类实例的值进行比较。而assertSame(Object expected,Object actual)内部逻辑判断使用了Java运算符“==”,这表明该断言判断两个实例是否来自于同一个引用(Reference),最好使用该方法对不同类的实例的值进行比对。asserEquals(String message,String expected,String actual)该方法对两个字符串进行逻辑比对,如果不匹配则显示着两个字符串有差异的地方。ComparisonFailure类提供两个字符串的比对,不匹配则给出详细的差异字符。

4)TestSuite测试包类——多个测试的组合
TestSuite类负责组装多个Test Cases。待测得类中可能包括了对被测类的多个测试,而TestSuit负责收集这些测试,使我们可以在一个测试中,完成全部的对被测类的多个测试。
TestSuite类实现了Test接口,且可以包含其它的TestSuites。它可以处理加入Test时的所有抛出的异常。
TestSuite处理测试用例有6个规约(否则会被拒绝执行测试)
A 测试用例必须是公有类(Public)
B 测试用例必须继承与TestCase类
C 测试用例的测试方法必须是公有的( Public )
D 测试用例的测试方法必须被声明为Void
E 测试用例中测试方法的前置名词必须是test
F 测试用例中测试方法误任何传递参数

5)TestResult结果类和其它类与接口
TestResult结果类集合了任意测试累加结果,通过TestResult实例传递个每个测试的Run()方法。TestResult在执行TestCase是如果失败会异常抛出
TestListener接口是个事件监听规约,可供TestRunner类使用。它通知listener的对象相关事件,方法包括测试开始startTest(Test test),测试结束endTest(Test test),错误,增加异常addError(Test test,Throwable t)和增加失败addFailure(Test test,AssertionFailedError t)
TestFailure失败类是个“失败”状况的收集类,解释每次测试执行过程中出现的异常情况。其toString()方法返回“失败”状况的简要描述。

八、JUnit的扩展应用 :

JUnit + HttpUnit=WEB功能测试工具
JUnit + hansel =代码覆盖测试工具
JUnit + abbot =界面自动回放测试工具
JUnit + dbunit =数据库测试工具
JUnit + junitperf=性能测试工具

九、测试的种类:

单元测试:检测模块(也就是类)的正确性。如果对象需要访问外部的数据资源,例如数据库,就需要模拟一个mock objects,但在实际中真实数据与测试环境是不同的。 客户测试:这是功能性、系统、和验收测试。用来测试整体的系统特性。在XP中,这些测试由用户编写。
综合测试:介于用户测试和单元测试之间的桥梁。综合测试帮助测试应用程序的交互性。一般情况下,mock objects不被用于综合测试,它会增加测试时间。同样,综合测
                    试经常依赖特殊的测试环境,例如数据库送来的测试数据。综合测试也需要用到外部类库。例如为J2EE应用程序进行综合测试的类库Cactus。
开发人员测试:这是用来让开发人员检验自己代码或新函数的。对于每一个开发人员,只要有可能,就需要有更多的测试来检验代码。组织这些测试和组织程序码一样
                            重要。


十、测试驱动开发(TDD)优势:

TDD的基本思路就是通过测试来推动整个开发的进行。而测试驱动开发技术并不只是单纯的测试工作。

需求向来就是软件开发过程中感觉最不好明确描述、易变的东西。这里说的需求不只是指用户的需求,还包括对代码的使用需求。很多开发人员最害怕的就是后期还要修改某个类或者函数的接口进行修改或者扩展,为什么会发生这样的事情就是因为这部分代码的使用需求没有很好的描述。测试驱动开发就是通过编写测试用例,先考虑代码的使用需求(包括功能、过程、接口等),而且这个描述是无二义的,可执行验证的。

通过编写这部分代码的测试用例,对其功能的分解、使用过程、接口都进行了设计。而且这种从使用角度对代码的设计通常更符合后期开发的需求。可测试的要求,对代码的内聚性的提高和复用都非常有益。因此测试驱动开发也是一种代码设计的过程。

开发人员通常对编写文档非常厌烦,但要使用、理解别人的代码时通常又希望能有文档进行指导。而测试驱动开发过程中产生的测试用例代码就是对代码的最好的解释。

快乐工作的基础就是对自己有信心,对自己的工作成果有信心。当前很多开发人员却经常在担心:“代码是否正确?”“辛苦编写的代码还有没有严重bug?”“修改的新代码对其他部分有没有影响?”。这种担心甚至导致某些代码应该修改却不敢修改的地步。测试驱动开发提供的测试集就可以作为你信心的来源。

当然测试驱动开发最重要的功能还在于保障代码的正确性,能够迅速发现、定位bug。而迅速发现、定位bug是很多开发人员的梦想。针对关键代码的测试集,以及不断完善的测试用例,为迅速发现、定位bug提供了条件。

我的一段功能非常复杂的代码使用TDD开发完成,真实环境应用中只发现几个bug,而且很快被定位解决。您在应用后,也一定会为那种自信的开发过程,功能不断增加、完善的感觉,迅速发现、定位bug的能力所感染,喜欢这个技术的。


十一、测试驱动开发(TDD)原理

测试驱动开发的基本思想就是在开发功能代码之前,先编写测试代码。也就是说在明确要开发某个功能后,首先思考如何对这个功能进行测试,并完成测试代码的编写,然后编写相关的代码满足这些测试用例。然后循环进行添加其他功能,直到完全部功能的开发。

我们这里把这个技术的应用领域从代码编写扩展到整个开发过程。应该对整个开发过程的各个阶段进行测试驱动,首先思考如何对这个阶段进行测试、验证、考核,并编写相关的测试文档,然后开始下一步工作,最后再验证相关的工作。下图是一个比较流行的测试模型:V测试模型。


【图 V测试模型】
【图 V测试模型】

在开发的各个阶段,包括需求分析、概要设计、详细设计、编码过程中都应该考虑相对应的测试工作,完成相关的测试用例的设计、测试方案、测试计划的编写。这里提到的开发阶段只是举例,根据实际的开发活动进行调整。相关的测试文档也不一定是非常详细复杂的文档,或者什么形式,但应该养成测试驱动的习惯

关于测试模型,还有X测试模型。这个测试模型,我认为,是对详细阶段和编码阶段进行建模,应该说更详细的描述了详细设计和编码阶段的开发行为。及针对某个功能进行对应的测试驱动开发。


【图 X测试模型】
【图 X测试模型】 


十一、测试驱动开发(TDD)过程:

软件开发其他阶段的测试驱动开发,根据测试驱动开发的思想完成对应的测试文档即可。下面针对详细设计和编码阶段进行介绍。测试驱动开发的基本过程如下:
1) 明确当前要完成的功能。可以记录成一个 TODO 列表。
2) 快速完成针对此功能的测试用例编写。
3) 测试代码编译不通过。
4) 编写对应的功能代码。
5) 测试通过。
6) 对代码进行重构,并保证测试通过。
7) 循环完成所有功能的开发。

开发过程中,通常把测试代码和功能代码分开存放,这里提供一个简单的测试框架使用例子,您可以通过它了解测试框架的使用。下面是文件列表。

	project/				项目主目录
project/test			        测试项目主目录
project/test/testSeq.cpp		测试seq_t 的测试文件,对其他功能文件的测试文件复制后修改即可
project/test/testSeq.h
project/test/Makefile			测试项目的 Makefile
project/test/main.cpp			测试项目的主文件,不需要修改
project/main.cpp		        项目的主文件
project/seq_t.h			        功能代码,被测试文件
project/Makefile		        项目的 Makefile
十二、测试驱动开发(TDD)原则:

测试隔离。不同代码的测试应该相互隔离。对一块代码的测试只考虑此代码的测试,不要考虑其实现细节(比如它使用了其他类的边界条件)。

一顶帽子。开发人员开发过程中要做不同的工作,比如:编写测试代码、开发功能代码、对代码重构等。做不同的事,承担不同的角色。开发人员完成对应的工作时应该
                    保持注意力集中在当前工作上,而不要过多的考虑其他方面的细节,保证头上只有一顶帽子。避免考虑无关细节过多,无谓地增加复杂度。

测试列表。需要测试的功能点很多。应该在任何阶段想添加功能需求问题时,把相关功能点加到测试列表中,然后继续手头工作。然后不断的完成对应的测试用例、功能
                    代码、重构。一是避免疏漏,也避免干扰当前进行的工作。

测试驱动。这个比较核心。完成某个功能,某个类,首先编写测试代码,考虑其如何使用、如何测试。然后在对其进行设计、编码。

先写断言。测试代码编写时,应该首先编写对功能代码的判断用的断言语句,然后编写相应的辅助语句。

可测试性。功能代码设计、开发时应该具有较强的可测试性。其实遵循比较好的设计原则的代码都具备较好的测试性。比如比较高的内聚性,尽量依赖于接口等。

及时重构。无论是功能代码还是测试代码,对结构不合理,重复的代码等情况,在测试通过后,及时进行重构。

小步前进。软件开发是个复杂性非常高的工作,开发过程中要考虑很多东西,包括代码的正确性、可扩展性、性能等等,很多问题都是因为复杂性太大导致的。极限编程
                    提出了一个非常好的思路就是小步前进。把所有的规模大、复杂性高的工作,分解成小的任务来完成。对于一个类来说,一个功能一个功能的完成,如果太困
                    难就再分解。每个功能的完成就走测试代码-功能代码-测试-重构的循环。通过分解降低整个系统开发的复杂性。这样的效果非常明显。几个小的功能代码
                    完成后,大的功能代码几乎是不用调试就可以通过。一个个类方法的实现,很快就看到整个类很快就完成啦。本来感觉很多特性需要增加,很快就会看到没有
                    几个啦。你甚至会为这个速度感到震惊。(我理解,是大幅度减少调试、出错的时间产生的这种速度感)

十三、测试驱动开发(TDD)测试技术:

1. 测试范围、粒度

对哪些功能进行测试?会不会太繁琐?什么时候可以停止测试?这些问题比较常见。按大师 Kent Benk 的话,对那些你认为应该测试的代码进行测试。就是说,要相信自己的感觉,自己的经验。那些重要的功能、核心的代码就应该重点测试。感到疲劳就应该停下来休息一下。感觉没有必要更详细的测试,就停止本轮测试。

测试驱动开发强调测试并不应该是负担,而应该是帮助我们减轻工作量的方法。而对于何时停止编写测试用例,也是应该根据你的经验,功能复杂、核心功能的代码就应该编写更全面、细致的测试用例,否则测试流程即可。

测试范围没有静态的标准,同时也应该可以随着时间改变。对于开始没有编写足够的测试的功能代码,随着bug的出现,根据bug补齐相关的测试用例即可。

小步前进的原则,要求我们对大的功能块测试时,应该先分拆成更小的功能块进行测试,比如一个类A使用了类B、C,就应该编写到A使用B、C功能的测试代码前,完成对B、C的测试和开发。那么是不是每个小类或者小函数都应该测试哪?我认为没有必要。你应该运用你的经验,对那些可能出问题的地方重点测试,感觉不可能出问题的地方就等它真正出问题的时候再补测试吧。

2. 怎么编写测试用例

测试用例的编写就用上了传统的测试技术。

  • 操作过程尽量模拟正常使用的过程。
  • 全面的测试用例应该尽量做到分支覆盖,核心代码尽量做到路径覆盖。
  • 测试数据尽量包括:真实数据、边界数据。
  • 测试语句和测试数据应该尽量简单,容易理解。
  • 为了避免对其他代码过多的依赖,可以实现简单的桩函数或桩类(Mock Object)。
  • 如果内部状态非常复杂或者应该判断流程而不是状态,可以通过记录日志字符串的方式进行验证。

 

posted on 2008-11-23 21:57 生命的绽放 阅读(1242) 评论(0)  编辑  收藏 所属分类: JUnit


只有注册用户登录后才能发表评论。


网站导航:
 
<2008年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

导航

统计

常用链接

留言簿(5)

随笔分类(94)

随笔档案(93)

文章分类(5)

文章档案(5)

相册

JAVA之桥

SQL之音

兄弟之窗

常用工具下载

积分与排名

最新评论

阅读排行榜