4.1.4 使得重构可行
没有单元测试的话,要证明重构是可行的将会是一件很困难的事,因为你总是可能损坏一些东西.为什么要花好几个
小时的时间只是为了改进实现设计,改变一个变量的名字诸如此类的事情呢?单元测试能提供一个安全网,它能为你提供重构的勇气.
4.1.5 改进实现设计
单元测试是客户要进行的代码测试的第一步.它们要求被测试的API是很灵活的,而且在孤立的状态下也是能进行
单元测试的.你通常需要重写你的代码来使它能进行单元测试.
4.1.6 当开发者文档来使用.
4.1.7 非常有趣
单元测试很容易让人上瘾.一旦你迷上了Junit绿色状态条,离开它会变得很困难.它给你带来了思想上的安宁.
刚开始的时候可能你会有所怀疑,但是一旦你迷上了测试,那么写代码而不写测试是会变成一件不可思议的事.
4.2 不同种类的测试
图4.2 大致地勾勒出了现在软件测试地五种类型,有其他划分软件测试地种类的方法.
4.2.1 软件测试的4种类型
我们已经提到郭了单元测试所关注的是每一个不同的工作单元.但是当几个不同的工作单元合并到一个工作流中的
时候,测试此时将要发生的事情会怎么样呢?工作流的后结果会是你所期待的吗?应用程序能够满足所有人的需要吗?
以上每一个问题将会由一个不同的软件测试类型来回答.针对讨论的问题,我们可以将软件测试分成四类:
集成测试
功能测试
压力/负荷测试
验收测试
集成测试
单个的单元测试是一个很重要的质量控制手段,但是当几个不同的工作单元合并到一个工作流中的时候,
将会发生什么事情呢?一旦你对一个类创建了一个测试并且已经执行了,下一步是关注其他方法和其他服务.检查组件之间的相互影响,这是进行集成测试.
因为可能你想要集成的东西有很多,集成这个词在不同的环境下代表了不同的事物.一些环境在表4.1中给出了.
功能测试
在对象之间的测试交互作用是很重要的.但是结果是你想要的那个吗/
功能测试在公共API的边界处的代码.通常情况下,这等于测试应用程序用例.
功能测试通常总是和集成测试结合在一块的.
压力/负荷测试
应用程序的功能能够正确执行是很重要的,但是当多个用户同时执行时的效果又会如何呢?大多数压力测试
检验应用程序是否能在短时间内响应大量的用户请求.通常,这是由一些特定的软件来执行.
比如:JMeter(http://jakarta.apache.org/jmeter).能够自动地发送设定好的请求以及跟踪应用程序的响应时间.通常请求是否被
正确的执行是不被测试的.
压力测试通常在一个单独的环境中进行.这种测试环境往往具有比典型的开发环境更多的控制.它必须跟运行的环境尽量相似.如果两个环境很不一样的话,测试也没有意义了.
其它类型的性能测试可以在开发环境下执行.
从测试的角度看,用profiler能够让你先知先觉.帮助你在真正的测试之前发现一些潜在的问题.首先你必须能够证明一个特定的瓶颈存在,然后你必须能够证明这个瓶颈已经被消除了.在两种情况下,profiler都是重要的工具.
单元测试也可以帮助你把一个应用程序分割成一个个的自然的部分.一些Junit扩展,比如JunitPerf能构帮助你建立一套跟你的单元测试相匹配的性能测试.你可以断言一个重要方法不能花太长的时间,作为性能单元测试的一部分你还可以指定一段时间作为执行的上限.当应用程序并重构,如果有什么变化影响到你的话,这些测试会向你报警.
验收测试
应用程序能够运行流畅是很重要的,但是终一个问题是:这个应用程序满足了用户的要求了吗?验收测试是这方面的测试.这些操作通常直接由用户或者用户的代理人来进行.验收测试是确保应用程序已经满足了用户提出的任何要求.
验收测试是所有其他测试的超集.
4.2.2 单元测试的3种类型
本书主要内容是通过程序进行自动单元测试.当我们使用单元测试术语的时候,主要关注的是从内部测试代码.这种行为不可避免的和编写代码相互联系并且同时发生.单元测试能够确保你的应用程序在一开始处在测试中.
当然,你的应用程序应该能够经受住其他各种软件测试形式.从单元测试开始,以验收测试结束.前面的章节介绍了对你的应用程序应当做的其他类型的测试.
许多应用程序都被分成许多的子系统.作为一个开发者,你想确保你的每一个子系统都能够正确地执行.当你写代码时,你的第一个测试大概是单元测试.当你写了越来越多的测试和越来越多的代码,你会发现,你需要开始添加集成和功能单元测试.在任何时刻,你都可能正在埋头于逻辑单元测试,集成测试或者是功能测试.
4.3 判断测试质量
4.3.1 衡量测试覆盖面
一种衡量方法是数一下你的测试中用到了多少方法.这不会告诉你你是否在做正确的事情,但它能告诉你是否有
测试.
没有单元测试的话,你只能依靠应用程序的公共API方法来写测试.因为你不需要仔细研究应用程序内部并以此来创造测试,这些被成为黑盒测试,图
单元测试的创建依赖于对一个方法实现的了解.如果在这个方法中存在一个条件分支,你能创建两个单元测试,每个分支各一个.你创建这样的测试需要仔细研究那个方法,这是被称为白盒测试.
有了白盒单元测试,要达到一个更高的测试水平变得简单一些,主要因为你已经访问了更多的方法而且因为你能控制每个方法的输入以及被调用的次级对象的行为(通过使用stub或者mockbojects你将会在后面的章节看到怎么做),白盒测试能够测试到包的保护方法和公有方法.
4.3.2 产生测试覆盖情况报告
对于Junit,可以找到一些工具帮你分析你的应用程序并且提供一个关于你的应用程序测试覆盖情况的确切报告.图4.7
给出了这样的一个报告,这个报告是由Clover(http://www.coretex.net/clover)工具创建的.
知道哪些类被进行了测试(DefaultController),知道哪些类还没有进行测试,这是重要的信息.但是如果能知道为什么DefaulgController只有94.7%的测试覆盖那更好了.幸运的是,clover能够在方法实现层次深入追踪.
知道哪些代码还没有进行测试是有好处的.尽管如此,一个好的测试工具(比如Clover)应该提供历史报告来显示整个开发过程中的测试经过,也应该提供与你喜欢的构建系统的集成.它也应该有能力来停止构建,如果标准没有达到的话.
在我们的项目中,我们喜欢进行一段开发迭代之后测试一下覆盖百分率.我们调整构建失败标准,以使下一次迭代至少有与前一迭代一样的覆盖百分率.这个策略确保了我们测试覆盖率能稳步上升.
虽然我们所展示的报告有帮助也很有趣,但是他们并不能告诉你你的测试有多么的好. 他们只会告诉你哪个方法已经被测试过了, 哪个还没有.算你的测试是完全错误的并且没有测试任何东西,你的报告仍然还是一样的!确保测试的质量是很困难的,测试工具能提供一定的帮助.Jester 是通过对被测试代码进行随意的变动来工作的;它会验证你的代码在这种情况下是否依然能通过.如果他们通过了, 那意味着测试还是够好.Jester通过多次反复来做这件事,它还会产生一个报告来揭示测试的质量.
记住的测试覆盖并不能保证你的应用程序得到了的测试,这一点是很重要的.你的测试覆盖只是和你的测试一样好!如果你的测试构想很糟糕,那么你的应用程序得不到足够的测试,不管你进行了多少测试.
4.3.3 测试交互
如果我们应用白盒测试能达到更高的测试覆盖,而且我们能够生成几个精彩的报告来证明这一点,那么我们还需要进行黑盒测试吗?
如果你想要完全的测试你的应用程序,包括运行对象是如何相互影响的,你需要把黑盒子测试作为整个测试的一部分.
4.4 测试驱动开发
在第三章中,你设计了一个应用程序,而且快速地创建了几个测试来验证你的设计.当你创建这些测试的时候,这些测试帮助改善了初的设计.随着你创建更多的单元测试,正反馈的良性循环会鼓励你尽早地编写单元测试.很快,当你设计实现的时候,要想知道你将要如何测试一个类变得自然而然.在这个方法论下,越来越多的开发者正在从利于测试的设计跃迁到测试驱动开发.