了解单元测试

本文是老书《软件测试与Junit实践》的笔记,翻看过就记录一下。

 软件测试

软件测试方法:需要快速组合运用到日常测试工作中

白盒测试、黑盒测试、针对测试、单元测试、集成测试、功能测试、系统测试、冒烟测试、回归测试、接受测试、负载测试、压力测试、可用性测试、安装/卸载~、性能测试、恢复测试、安全测试、兼容测试、Alpha测试、Beta测试

验收测试>>压力负荷测试>>功能测试>>集成测试>>单元测试。

【定义-测试】

发现并指出软件中存在缺陷的过程,这个过程指明和标注问题存在的正确位置,详细记录导致问题出现的操作步骤,及时储存当时的错误状态,以上组合在一起便于测试后问题能够准确再现。[发现问题、记录问题、跟踪问题、历史数据]

[测试可用资源、测试时间、测试范围],软件工程全过程中所有环节的测试

 

【软件测试原则】

1)尽早和不断地测试

2)彻底的测试不可能

3)由小到大的测试范围

4)避免检查自己的代码

5)追溯至用户需求

6)考虑到各种输入

7)错误集中发生现象

8)跟踪测试错误结果

9)合理安排测试计划:[测试需求、测试策略[测试类型、方法、工具]、资源、项目里程碑、可交付工件[测试模型、测试记录、缺陷报告]]

10)错误的关联和依赖

11)测试结果的全面检查

12)及时更新测试

 

 

可测试性

如果要对软件进行有效的测试,首先应该确认该软件是可测试的,可测试反映了软件内在的一个私有属性,不会因为使用了某些测试工具进行了测试,就使得被测试软件具有了可测试性。    可测试性也体现了软件的品质,当前软件设计讲究高内聚、低耦合、接口明确、意图明晰,如果软件表现为高耦合或逻辑混乱,那么这样的软件是不适合使用测试工具的。要想真正获得测试工具带来的好处,并且使得工具能够发挥最大的效率,关键就是要使软件本身具有很好的可测试性。

 

【测试认识的误区

1)测试工具和有效测试

2)测试天生是矮子

3)随意的测试:测试的每道工序都是严谨的,造成这种现象是测试意图过于模糊,要对测试目标进行准确定位。测试的目的是用来验证软件是否满足最终用户的需求。

4)测试是一种想象:要科学、客观地测试和软件Bug分析

5)非专业人员做测试

6)测试是简单的事情

7)测试能保证软件质量:不能保证,只能客观反映某个时间段内软件的质量

 

 

测试计划à设计测试à执行测试à集成测试à系统测试à测试评估

 

单元测试

【单元测试为什么】

1BUG关联:在面向对象的领域中,缺乏测试程序的编码过程是及其危险的,即使是高内聚、低耦合的设计同样会产生许多BUG。实现人员修改某些BUG,因为BUG存在的关联特性会引发更多的其他BUG。大多数情况下,实现人员忙于开发、忙于修改BUG,最终导致整个软件系统因为大量BUG的积压而崩溃。

2XP开发XP极其依赖于单元测试,并且单元测试也是XP实践中的关键一项。单元测试为XP提供了一种保障,所有实行XP过程项目组的单元测试都是随编码一起发行的,并且XP要项目组保证在后续的增量开发过程中,每一次代码发布都需要执行以前的单元测试,保证所有的测试都是100%通过。         单元测试支持变化,因为任何变化所导致的失败情况都会被立刻反映出来,提高软件系统整体可信赖度。

3提高开发速度和质量:①应对需求变化,考虑到项目后期的维护和解决累计BUG的时间,那么在项目中充分介入单元测试,就可以节省大量的宝贵时间,让实现人员对代码本身有更大的掌握力。②测试都是以自动化方式执行的,测试代码随着相对被测试代码的增加而增加,这里也体现了一个测试覆盖范围的问题,所有的测试都会连带执行以前积累的测试代码[回归测试],消除了因为新代码的使用造成旧代码被破坏的现象。也保证代码重构的正确性。

代码测试是一个极佳的保护伞,它除了帮我们检查代码的输出是否符合期望值外,还支持重构技术的应用,这些都得到提高了软件代码质量。同时,单元测试还缩短了发现BUG到消除BUG的总时间量,变相提供了软件开发速度

4单元测试也是一种设计:先测试后编码思想的应用。引入测试使我们将更多地关注于该软件的具体功能实现是否符合原设计,而不仅仅是定位于实现的内部代码运作机制上:单元测试是一种分析设计的过程,并且这种过程可以催生出设计优良和结构紧凑、便于测试的代码体。           测试代码和实现代码的书写过程相互交错,可以随时把握代码质量。 如果一个单元测试太长或者太笨拙,那通常就意味着被测试的代码在设计上存在一些问题,应该对它进行重构。如果一个测试无法孤立地验证一个特性,那通常意味着代码不够灵活,也需要重构。

5可文档化、交流:其他人可以通过单元测试,就能非常清晰、明白和容易地掌握各个代码块/类的具体功能。将预期的行为文档化。各种框架的官方文档往往不仅会提供api还会提供测试用例理解如何使用api。

 

有利于:

编码前的思考思路:设计,文档。。。

编码开发时应对:重构,代码改动,各种改动。。。

编码后恐惧压力:bug,集成测试,交接工作。。

 

【被测试的代码】

对象的所有具体行为都要被测试,当然做全部的测试有点不切实际,这只根据具体操作者的经验来确定。Settergetter可以不用。

 

【单元测试的误区】

CRCClass, Responsibility and Collaboration:类,责任和交互,CRC卡片。在OOD中,用来阐述类、类的行为和类的责任的一个非常好的途径。

 

       测试前期没有CRC可能导致单元测试成为一种简单功能应用的展示,很多实现人员完成编码后面临测试任务,他们回忆代码并找到它做了什么,然后书写测试证明代码做了什么。

       首先写好CRC,这样测试就能够以严谨的测试规格说明为基础。代码就能够针对它的规格约定,而不是针对自身进行测试。

       在实践中会出现这样的情况:大部分实现人员面对单元测试只是针对于代码,而并非针对于测试规格说明。           如何做?要理解这个被测试单元原本要做什么,而不是它实际上做了什么。实现有效的CRC制度,在CRC文档中仔细描述针对该单元调用和被调用的相关设计逻辑,根据这些逻辑边界给出相应的测试方法和测试数据。画出程序流程图。

 

测试驱动开发

 

1)编写单元级测试代码[体系设计或声明公共接口]

2)编写代码通过单元级测试[测试设计]

3)重构的运用

4)重新运行测试[不产生BUG,不破坏原设计,不断调整编写代码]

 

 

【集成测试VS单元测试】

规模越大的代码集成意味着其复杂性就越高。

切入单元测试是一种思想,将被测试单元尽量划分成细小、功能集中、便于检查和排错。

集成测试可能没有单元测试的广泛覆盖性,创造一种单元调用的测试条件的时候,要全面考虑单元被调用时的各种入口参数,对单元功能全面测试的复杂程度远远超过独立进行的单元测试过程。最后的结果是测试将无法到达它所应该有的全面性,一些缺陷将被遗漏,并且很多BUG将被忽略。拆开进行机械修理,比在外观上敲敲打打的效果要和得多。

 

 

其他

【测试用例】=期望值+执行条件+实际值

类型:需求测试用例、设计测试用例、代码测试用例

输入数据:正常数据、边界值和错误数据

 

【软件复审】

对软件工程过程任意阶段产生的任意正式工件进行的复审活动。复审贯穿于软件过程中的所有阶段,是软件工程过程中的“过滤器”,起到发现问题、排除错误的作用。

正式技术复审:不包括工件生产者本人在内。

内容:需求复审[业务逻辑复审]、设计复审[程序设计和结构的复审]、代码复审[代码风和规则的复审]

误区1)参与人员不了解评审;2)评审目标偏移:目的是发现问题,不是评价相关人员的水平;3)评审没有被安排进项目开发计划;4)评审会议变成问题解决方案讨论会;5)评审人员事先对评审工件没有足够了解;6)评审人员关注于非实质性问题;7)忽视组织细节;8)会议时间过长。

 

【测试自动化】大部分用于回归测试

2种极端:完全自动化,全部不用人工干预的测试       仅能用一次,用完就丢的测试

前提1)确认要测试的功能点;2)确定在测试的过程中所要验证的信息;3)估计要测试的事件的类型;4)考虑通过参数化固定值来增加你的测试的效率和适应性;5)确定如何组织对象库文件;6)确定流线型测试过程。

相关名词:中介代码、具有黑盒测试特征的功能测试、具有白盒特征的单元测试

测试工具的实现:大部分的商业黑盒测试工具都具有脚本自动录制、回放的功能,这些功能包装了以相对较低的资源消耗,全自动执行软件测试和回归测试计划。分对象识别模式和动作识别模型。

 

 

类测试

类测试=+测试体Tester

框架:测试驱动TestDrive1——n测试用例TestCase1——n测试描述Test

 

类测试除了要测试“类的方法”还要测试“类的状态”。

是否每个类都需要进行单独的测试,还是绑定多个类进行集成测试,这些都取决于类的测试价值。评估每个类的测试价值,可以使用3个评估要素:①类在系统中的层次,越上层价值越高;②类本身复杂程度和与外体间的交互复杂程度;③开发该类的测试程序所需的成本[时间|人力|其他资源]

 

【确定类测试用例】

类测试用例的确定和构造取决于准确的类描述,而类描述可能包含类UML设计、类CRC卡片和类状态机等工件。

1)根据前置和后置状态确定测试用例

2)根据状态转换确定测试用例

3)根据代码确定测试用例,如访问符

 

【构造类测试驱动】

RUP中测试的概念是测试优先于设计,而在XP中则表现为测试优先于编程。测试驱动的基本思想是在对设计/编码之前先考虑好/写好测试代码。这样测试工作就不仅仅是测试,而成为设计/代码的规范了。

       类测试驱动器是运行一个或者多个测试用例的可执行程序,编写类测试驱动有3种:

Main方法

所见即所得,快速得知测试后结果

不利于后期测试代码的维护

不利于测试代码的复用

交付后的程序必须逐个剔除测试编码

嵌入静态测试方法

测试代码调试方便,被测试类无须实例化就可以进行测试

同上

实现独立测试类

测试代码可复用

测试用例独立于被测试类之外,利于测试代码的维护

经过改进开发,可以一次性执行多个测试用例

需要评价被测试类的测试价值,生成新的测试类

需要密切关注被测试类的变化

 

TestCase

成员变量_CUTname_OUT _passTally _failTally

成员函数

1TestCase, dispose

2runAllTestSuites, runFunctionalTestSuites, runInteractionTestSuites, runConstructTestSuites

3passTally, failTally, totalTally

4newObject, disposeObject, setObject, getObject

5logTestCaseStart, logTestCaseResult, logComment

6passOrFial

构建测试用例、执行测试用例、分析测试用例结果,创建用于运行Object实例

测试方法的目的:通过创造输入状态、生成事件序列并检查输出状态来执行测试用例的,每个测试用例报告结果的状态为通过或失败。[这些测试用例方法为测试用例构建了输入状态,例如使用newObject, setObject, getObject方法进行被测试类实例化,并把生成的实例以参数化形式传递,最后这些测试用例方法统一形成了排队的测试队列]

测试方法命名:①test+用例方法名称;②根据前置条件和后置状态命名

类型:子类体系、接口类、抽象类、内部类、异常

 

JUnit3

相关类:Test接口,TestCaseTestSuiteTestResultTestListenerProtectableTestFailureAssertComparisionFailureRunnerawtuitestuiswingui

一些规约:

1)测试用例类必须是共有类,继承与TestCase

2)测试方法必须是public,返回值是void,方法名前缀test,无方法参数。

类图又见百度百科Junit。使用了组合模式,testtestcasetestsuite

设计原则:

1)不要测试简单的事

2)测试任何可能出现错误的地方

3)测试边界条件:未初始化,Null值,最大值,最小值,临界值,初始值

4)作为详细设计文档和类文档的衍生

5)自动化

6)必须100%通过

7)测试重用

8)测试用例应该独立

9)测试依赖于接口

10)固定类方法的调用顺序

 

【测试延伸】

1)多个用例的方法之间存在着组合调用和执行的先后顺序,而Junit则显得对单个用例的测试更有效果,所以就需要改造一下Junit的测试方式来适应这些情况:引入一个独立的Recorder类用来录制ClientServer的执行动作,在一个标准测试用例TestCase中统一验证Recorder的结果是否和预期值相等。

2抽象类测试:①Junit对抽象类的测试引入工厂设计模型,其测试思想是:抽象类不能被实例化,所有使用具体类测试抽象类是不可行。因此,构造抽象类的测试类必须也是抽象的并且继承JunitTestCase类。该类需要强调声明两种类型的抽象方法。第一类抽象方法即工厂方法,返回所有被测试抽象类的具体子类实例,第二类定义抽象方法返回所有被测试抽象类的具体子类行为期望值。    ②如果抽象类中包含了具体实现方法,那么使用上面的抽象类测试方法就很勉强,因为抽象类的具体方法有可能被继承该抽象类的具体子类所覆盖,导致测试偏差现象的发生。对于这样的测试场景,可以引入静态内部进行抽象类变相实例化测试。

3私有方法的测试:使用反射机制?

4映射对象Mock:简单模拟真实的实现,可用于基础前置、传递环境、固定状态

许多有价值的代码难以被单独测试,这些代码更多地表现为一种粘联执行,而Mock隔离了这些被测试代码之间,被测试代码和测试代码之间的关联程度,可以方便地进行单元级测试。   

Mock是将被测试代码替换成映射对象来实现真实代码的功能,这些MockObjects被传回原来的被测试代码中,这样就构成了被测试代码的内部测试。

MockObject是其他被测试代码行为的映射或者中介物的实现替代,它应该比真实的代码更坚定,而不是复制它的所有实现,Mock允许使用者建立私有的状态来协助测试。Mock实现的重点是绝对的简单化而非完整化。

5MockObject和重构

       实现人员总要写一些依赖其他对象的代码实现,而MockObjects是一个非常好的为对象之间发现接口的技术。

6MockObject要点:①识别所有创建或得到的协作代码,注意这些代码都是相互联系、彼此使用消息机制关联的互连体;②对这些代码应用Extract Method重构,创建合适的工厂方法;③包装工厂方法能够达到目标对象和它的子类;④在测试代码中,创建MockObject以实现协作者的接口;⑤在测试代码中,创建一个特殊的对象扩展目标;⑥在这个特殊对象中,复写创建方法,返回一个测试用例到MockObject;⑦可选:创建一个单元测试来保证最初的目标对象的工厂方法还返回正确的对象,而不是MockObject

7)异常处理

8)随机测试

9)间隔测试

 

JUnit4 @

相关类:

1RunnerFilterSorter

 

相关注释:几乎都要求是public、无返回、不带参

@BeforeClass@AfterClass:静态方法 throws Exception

@Before@After:throws Exception

@Test

@Ignore

顺序:@BeforeClass@Before@Test@After@Before@Test@After @AfterClass

 

1测试:测试方法必须使用@Test注释,是公共的,不带任何参数,并返回void类型。

 

2测试类:一个包含一个或者多个测试的类。

JUnit在调用执行每个@Test方法之前,为测试类创建一个新的实例:有助于提供测试方法之间的独立性,并避免在测试代码中产生意外的副作用。

 

3测试集suite:一组测试。如果没有为测试类定义一个测试集,那JUnit会自动提供一个测试集,包含测试类中所有的测试。

 

4测试运行器test runner:执行测试集的程序。

管理测试类的生命周期:创建类、调用测试、搜索结果。

类型:参数化Parameterized的测试运行器:运行使用不同的参数多次运行同一个测试。

 

 

JUnit4提供的测试运行器:

JUnit38ClassRunner:仅为向后兼容,将测试用例作为JUnit3.8的测试用例来启动

JUnit4:将测试用例作为JUnit4的测试用来启动

Parameterized:可以使用不同参数来运行相同的测试集

Suite:是一个包含不同测试的容器,同时也是一个运行器,运行一个测试类中所有以@Test注释的方法。

如果测试类中没有提供任何运行器,那么JUnit将会使用一个默认的运行器,如果希望JUnit使用某个特定的测试运行器,那么就使用@RunWith注释来指定测试运行器类。

JunitCore:可以运行任何测试运行器,是JUnit提供的一个façade门面。Junit设计这个Façade来执行测试并收集测试结果与统计信息。

 

@Before @After:方法必须公有

@BeforeClass @AfterClass:方法必须公有且静态

@Test注释添加expected参数,来指定你所预期的异常类型

@Test中的另一个参数timeout:超时测试,性能测试

引入Hamcrest匹配器

 

最佳实践:

1) 测试类的包应该和被测试类保持一致

2) 测试单元中的每个方法必须可以独立测试,测试方法间不能有任何依赖

3) 测试类使用Test作为类名的后缀

4) 测试方法使用test作为方法名的前缀

5) 尽量使用assert...测试预期结果

6) 测试用例不是用来证明你是对的,而是用来证明你没有错

7) 测试结果的3个结果状态:success, failure, error

 

 

 

另外文章还可以参考:

1Java单元测试:http://blog.thihy.info/post/103

2JUnit4源码解析:http://www.blogjava.net/DLevin/archive/2012/05/11/377950.html

 

猜你喜欢

转载自bravecs.iteye.com/blog/1775411