第六章:用stub进行粗粒度测试
本章内容:
介绍stub
使用嵌入式服务器代替真正的网络服务器
用stub单元测试一个HTTP连接案例
当你开发自己的应用程序时,你可能会发现你想要测试的代码段依赖于其它的类,而它们本身也依赖于另一些类,这些类则要依赖于开发环境.
由于程序依赖于开发环境,编写单元测试成为了一个挑战.你所做的测试需要具有稳定性,当你一遍又一遍地运行它们,产生的结果必须是一致的.所以,你需要找到一个方法来控制运行环境.一个解决的办法是建立真正的需求环境作为测试的一部分,并在其中运行测试.在某些情况下,这种方法是可行的,同时带来了额外的价值.但是,只有你在开发平台上建立真实环境,测试才工作得好.事实上,这种情况不是总是会出现的.
例如,你的应用程序使用http连接由第三方提供web服务器,但是在你的开发环境里通常并不存在那样一个可用的服务器程序.所以,你需要一种方法模仿服务器,这样便可以编写测试代码了.
还有另外一种情况,假设你同其它开发者一起开发一个项目.你想测试项目中你的那一部分.但其中部分还没有完成,那该怎么办呢?解决的办法是用一个仿造品模拟缺失的部分.
这里有两个策略,供我们生成模拟对象:stub技术和使用mock objects. Stub是个原始的方法,但如今仍很流行,很大程度上是因为,它们使得你可以测试代码,而不必特意为了测试而修改代码,Mock objects则是另外一种情况.在这章我们专门介绍stub技术,第7章讲到mock objects.
6.1 stub简介
Stub这种机制是用来模拟可能存在或还没写完的真实代码所产生的行为.它使你能顺利地测试系统的一部分,而无须考虑其他部分是否可行.通常,stub不会改变你测试的代码,只是加以适配以提供无缝整合.
Stub----stub是代码的一部分.在运行时我们用stub替换真正代码,忽略调用代码的实现.目的是用一个简单一点的行为替换一个复杂的行为,从而允许独立地测试代码的某一部分.
这里有一些用到stub的例子:
你能不修改一个现有的系统,因为它很复杂,很容易崩溃.
粗粒度测试,如在不同子系统之间进行集成测试.
通常,stub给测试的系统以相当好的可靠性.使用stub,你并没有修改被测试的对象,你所测试的对象同将来产品中要运行的一样.用stub进行测试一般是运行环境中完成的.这保证了系统运行的可靠性.
从另一面来说,stub通常难以编写,尤其当仿真系统很复杂的时候.stub需要实现和替代的代码一样的逻辑,而准确地再现复杂逻辑是一件很困难的事.而结果常常是需要调试stub!下面列出了不使用stub的理由:
Stub常常会很复杂,它们本身需要调试.
因为stub的复杂性,它们可能会很难维护
Stub不能很好的运用于细粒度测试.
不同的情况需要不同的策略.
一般而言,stub更适合代替代码中粗粒度的部分.你通常会愿意用stub代替成熟的外部系统,诸如,文件系统,到服务器的连接,数据库等.用stub替代对单一类的方法调用可以做到,但是比较难实现.
6.2 一个HTTP连接的例子
为了演示stub能做些什么,我们为一个简单的程序创建了一些stub,它根据URL打开了一个http连接,同时读取其中的信息.图6.1显示了一个程序例子,它通过HTTP连接远程web资源.
在这一章里我们的目标是通过用stub替换远程web资源来对getContent方法执行单元测试.
这种方法允许你独立地测试执行web资源的getContent方法.
关于stub重要的一点是,getContent没有为接收stub而作修改.对于被测试的程序而言是透明的.为了实现这点,被替换的外围代码需要有定义完善的接口,并允许插入不同的实现.
让我们来以一个简单HTTP连接的例子来看看stub是如何运行的把.代码6.1中的样本程序给出了一个代码片断,它为给定的URL打开HTTP连接,读取找到的URL上的信息.设想一下,这个方法是你想对其执行单元测试的一个大项目的一部分.现在,让我们对这个方法进行单元测试吧.
6.2.1 选择一个替换方案
在案例程序中有两个可能的情况:远程web服务器位于开发平台的外围,或者,本身是程序配置平台的一部分.不管怎样,为了能够对webClient类进行单元测试,你必须在开发平台上建立一个服务器.相对容易的解决办法是为其安装一个Apache测试服务器,在它的文档根目录下放一些测试web页面.这是典型的,广泛使用的替换方法.但它有缺陷:
依赖环境---在测试前确保运行环境已经准备好了.如果web服务器关闭了,但测试被执行了,结果必然是错误的.那时你便会试着检查出错的原因.接着,你发现代码工作正常—这只是运行环境的问题,导致一个错误的警告.这种事情既浪费时间又令人厌烦.所以,你在单元测试时,重要的一点是尽可能地控制测试执行中的环境,这样才能保证测试结果的可再现性.
分散的测试逻辑---测试逻辑被分散到两个不同的地方:一是在Junit TestCase,二是测试web页面.这两种资源都需要在测试中保持同步.
测试难以实现自动化—自动执行测试还是很困难,因为它需要在web服务器上自动配置web页面,自动启动web服务器,而完成这一切仅仅是为了运行单元测试.
幸运地,有一个更好的解决办法---使用嵌入式服务器.你在java中进行测试,所以容易的选择是使用可以嵌入test
Case类的Java web服务器.确实存在这样的好东西,它叫Jetty.
为什么是Jetty?因为它有较快的运行速度,它是轻量级的,而且在java中可以从test case中完全控制其运行.另外,它还是一个很好的web/servlet容器,可以在产品中使用它.这对于测试而言不是特别重要,但是使用好的技术始终是一个好的策略.
使用Jetty能让你消除前文提到的不足之处:服务器从Junit test case开始运行,所有测试都在同一个位置用java编写,把test suite自动化也成了一个微不足道的问题.得益于Jetty的模块性,要做的事情只是用stub替换Jetty处理器,而不是替换整个服务器.
6.2.2 用Jetty作为嵌入式服务器
为了更好地了解如何从测试中建立和控制Jetty,我们实现了一个从Java代码中启动Jetty的简单例子.代码6.2演示如何从java中启动以及如何定义一个文档根目录(/)以启动服务文件.图6.3说明了当你运行程序并在URL http://localhost:8080上打开浏览器的结果.
代码6.2以嵌入模式启动Jetty――JettySample类