本书通过介绍一系列开源Java开发工具和生动的实例,描述如何用极限编程(eXtreme Programming)理论指导具体的开发行为,从而使Java开发更加灵活、高效、低成本。
本书主要讲述J2EE和极限编程的基础,重点介绍如何掌握极限编程过程中最难的部分:测试、整合和部署。全书以基础开篇,接着辅以范例并对著名的宠物商店范例进行功能性剖析。在了解了各个工具的概念后,作者介绍了测试的实践,一步一步指导读者掌握极限编程开发的技术要点。
本书内容丰富、详实、生动,适合作为Java软件开发人员的参考书。
考虑到富有经验的Java程序员的需要,本书首先简短介绍极限编程方法及技术,随后探究本书始终沿用的一个应用程序范例来体验工具的作用和开发活动中的实践。作者在介绍每一个工具时都提供关键概念的描述以及代码范例,并一步一步带领读者掌握极限编程开发方面的技术。
本书覆盖了以下极限编程的主题:
自动单元测试和功能测试
通过自动构建和自动部署进行持续整合
重构和持续整合的价值
如何用Ant、JUnitPerf、Cactus,HTTPUnit和JMeter来实现极限编程中的目标
相关的网站包含以下内容:
本书中涉及的范例代码
极限编程软件工具的升级版本
实用的极限编程站点链接
软件界信奉的哲学是“不变则亡”。极限编程为敏捷地应对软件开发中的各种变化提供了全新思路,本书是Richard Hightower和Nicholas Lesiecki关于极限编程(Extreme Programming)的创新之作, 讲述了这种能使开发人员快速、有效、低成本地创建灵活高效软件的方法论、本书将为您演示如何使用开源(Open Source)Java开发工具实现Java开发中的极限编程方法,并使大家掌握极限编程中最复杂的开发过程:测试、整合及部署。
译者序\r\n致谢\r\n作者简介\r\n前言\r\n第一部分 关键概念介绍\r\n第1章 Extreme Programming介绍 2\r\n1.1 极限编程概览 2\r\n1.1.1 极限编程开发方法的四大价值 2\r\n1.1.2 极限编程的五个原则 3\r\n1.2 极限编程的12个实践原则 5\r\n1.2.1 计划的制定 5\r\n1.2.2 小版本 5\r\n1.2.3 简单设计 6\r\n1.2.4 测试 6\r\n1.2.5 持续整合 6\r\n1.2.6 重构 7\r\n1.2.7 配对编程 7\r\n1.2.8 代码共享 8\r\n1.2.9 每周只工作40小时 9\r\n1.2.10 现场客户 9\r\n1.2.11 隐喻 9\r\n1.2.12 编码标准 9\r\n1.3 采用极限编程吗 9\r\n1.4 小结 10\r\n第2章 J2EE 部署 12\r\n2.1 概述 12\r\n2.2 JAR文件 14\r\n2.2.1 JAR文件和applet 14\r\n2.2.2 可执行的JAR文件 15\r\n2.3 Web应用程序和WAR文件 16\r\n2.4 EJB和EJB JAR文件 18\r\n2.5 企业级应用程序和EAR文件 21\r\n2.6 小结 21\r\n第3章 应用程序范例 23\r\n3.1 概述 23\r\n3.2 简单范例:基于Model2的HelloWorld 23\r\n3.3 范例:宠物商店 24\r\n3.4 宠物商店的最初版本 25\r\n3.4.1 model类(公共的接口) 25\r\n3.4.2 dbmodel类(实现) 26\r\n3.4.3 数据库模型 27\r\n3.4.4 Web接口 28\r\n3.4.5 构建系统 31\r\n3.5 用测试数据设置数据库:Ant小试牛刀 34\r\n3.6 完整的代码清单 39\r\n3.6.1 model包的完整代码清单 39\r\n3.6.2 dbmodel包的完整代码清单 43\r\n3.6.3 test包的完整代码清单 49\r\n3.6.4 JSP和Web.xml的完整代码清单 55\r\n3.7 小结 60\r\n第二部分 掌握工具\r\n第4章 使用Ant进行持续整合 62\r\n4.1 概述 62\r\n4.2 Ant使用基础 63\r\n4.3 标准目标 65\r\n4.3.1 简单的范例 65\r\n4.3.2 使用特性一起运作 67\r\n4.3.3 路径设置和fileset设置 68\r\n4.3.4 条件性目标 70\r\n4.3.5 使用过滤器 72\r\n4.3.6 嵌套构建 73\r\n4.4 小结 73\r\n第5章 使用Ant建立Java应用程序 75\r\n5.1 概述 75\r\n5.2 Hello World Model项目 75\r\n5.2.1 model类概览 76\r\n5.2.2 为Model建立项目目录结构 77\r\n5.2.3 为共享类库建立构建文件 77\r\n5.2.4 model项目构建文件的分析 78\r\n5.2.5 运行Ant构建文件 81\r\n5.3 Hello World应用程序项目 82\r\n5.3.1 应用程序的Java类概览 82\r\n5.3.2 为应用程序建立项目目录结构 83\r\n5.3.3 为独立应用程序建立清单文件 83\r\n5.3.4 为独立应用程序建立Ant构建文件 83\r\n5.4 Hello World主项目 86\r\n5.4.1 建立主构建文件 86\r\n5.4.2 关于主构建文件的分析 87\r\n5.5 applet项目 88\r\n5.5.1 applet类概览 88\r\n5.5.2 为applet程序建立构建文件 91\r\n5.5.3 使用Ant构建applet程序 92\r\n5.6 Hello World 功能翻新 93\r\n5.7 小结 93\r\n第6章 使用Ant建立J2EE应用程序 94\r\n6.1 概述 94\r\n6.2 Hello World 94\r\n6.3 Web应用程序项目 95\r\n6.3.1 Web应用程序项目的目录结构 95\r\n6.3.2 HelloWorldServlet.java 96\r\n6.3.3 HelloWorldServlet分析 97\r\n6.3.4 HelloWorld.jsp 98\r\n6.3.5 HelloWorld Web应用程序的部署\r\n描述符 98\r\n6.3.6 HelloWorld Web应用程序的构建文件 99\r\n6.3.7 构建和部署Web应用程序 105\r\n6.3.8 运行Web应用程序 107\r\n6.3.9 HelloWorld.jsp applet 传递 108\r\n6.3.10 在WAR文件中包含applet 109\r\n6.4 企业级JavaBean 110\r\n6.4.1 企业级Bean项目的目录结构 110\r\n6.4.2 HelloWorld实体Bean 111\r\n6.4.3 会话Bean 113\r\n6.4.4 Shadow代理Bean 116\r\n6.4.5 企业级JavaBean部署描述符 118\r\n6.4.6 企业级Bean的构建文件 120\r\n6.4.7 企业级Bean构建文件的分析 123\r\n6.4.8 在Web应用程序构建文件中定义\r\nejb特性 125\r\n6.4.9 运行构建文件 126\r\n6.5 EAR 企业级应用程序 127\r\n6.5.1 HelloWorld主项目构建文件 127\r\n6.5.2 企业级应用程序构建文件的分析 129\r\n6.5.3 企业应用程序部署描述符 131\r\n6.6 宠物商店案例学习 132\r\n6.7 小结 134\r\n第7章 使用JUnit进行单元测试 136\r\n7.1 JUnit概述 137\r\n7.1.1 编写一个测试案例 138\r\n7.1.2 使用Ant整合JUnit 143\r\n7.2 案例研究:在宠物商店案例中\r\n加入实体Bean 149\r\n7.2.1 现有的JUnit测试概述 149\r\n7.2.2 在宠物商店案例中加入EJB 157\r\n7.2.3 建立一个Ant构建文件用以部署\r\n我们的实体Bean 169\r\n7.2.4 修改Test构建文件用以测试\r\n我们的实体Bean 175\r\n7.2.5 学习案例的小结 177\r\n7.3 小结 177\r\n第8章 使用Cactus测试容器服务 178\r\n8.1 概述 178\r\n8.2 为什么要做容器内测试 178\r\n8.2.1 隔离测试:模拟对象 178\r\n8.2.2 上下文测试:整合测试 179\r\n8.2.3 一个混合手法 180\r\n8.3 为什么使用Cactus 180\r\n8.4 安装Cactus 181\r\n8.4.1 服务器端安装 182\r\n8.4.2 客户端安装 183\r\n8.4.3 一个简单的范例 184\r\n8.5 Cactus系统架构 185\r\n8.5.1 开始执行 185\r\n8.5.2 beginXXX()方法 186\r\n8.5.3 调用转向器Servlet 186\r\n8.5.4 服务器端安装 186\r\n8.5.5 服务器端执行 187\r\n8.5.6 结果集合和后处理 187\r\n8.6 编写Cactus测试 188\r\n8.6.1 被测试的代码 188\r\n8.6.2 继承适当的类 190\r\n8.6.3 beginXXX()方法 190\r\n8.6.4 隐含对象 191\r\n8.6.5 setUp()和tearDown() 192\r\n8.6.6 testXXX() 193\r\n8.6.7 endXXX() 193\r\n8.7 Cactus与Ant的使用 195\r\n8.8 Filter测试 196\r\n8.9 JSPTestCase和自定义标记 198\r\n8.10 案例学习:使用自定义标记的\r\n宠物商店程序 213\r\n8.10.1 业务需求 213\r\n8.10.2 寻找一个开始点 213\r\n8.10.3 测试案例 214\r\n8.11 小结 219\r\n第9章 使用HttpUnit进行功能测试 220\r\n9.1 为什么要做功能测试 220\r\n9.2 为什么要使用HttpUnit 221\r\n9.3 HttpUnit基础 222\r\n9.3.1 WebClient(客户端)功能介绍 222\r\n9.3.2 响应检查和多页面处理 223\r\n9.4 HttpUnit的高级话题 231\r\n9.4.1 DOM检查 232\r\n9.4.2 Header和Cookies 233\r\n9.4.3 框架页面 233\r\n9.4.4 SSL 234\r\n9.4.5 HttpUnitOptions 234\r\n9.4.6 技术局限性 234\r\n9.5 蜘蛛范例程序 234\r\n9.5.1 蜘蛛程序开发:第一个循环 235\r\n9.5.2 蜘蛛程序开发:二次循环 236\r\n9.5.3 蜘蛛工具将来的工作 240\r\n9.6 小结 240\r\n第10章 使用JMeter测试应用程序性能 241\r\n10.1 概述 241\r\n10.2 JMeter概述 242\r\n10.3 JMeter概念 242\r\n10.4 使用JMeter测试Web应用程序\r\n浏览程序 244\r\n10.5 使用JMeter测试Web应用程序\r\n的表单条目 249\r\n10.6 使用JMeter测试我们的RDBMS\r\n的性能 255\r\n10.7 案例学习:宠物商店 257\r\n10.7.1 业务需求 257\r\n10.7.2 建立测试 259\r\n10.7.3 分析 262\r\n10.7.4 结论 263\r\n10.8 小结 263\r\n第11章 使用JUnitPerf进行负载测试 264\r\n11.1 概述 264\r\n11.2 JUnitPerf概念 264\r\n11.3 TimedTest范例 265\r\n11.4 LoadTest范例 272\r\n11.5 案例学习 273\r\n11.5.1 HTTPUnit测试 273\r\n11.5.2 JMeter配置 275\r\n11.5.3 将它们放在一块 275\r\n11.5.4 结论 278\r\n11.6 小结 279\r\n第三部分 API和标记参考\r\n第12章 Ant标记参考 282\r\n12.1 Ant命令行选项 282\r\n12.2 Ant的父元素 283\r\n12.2.1 project标记 283\r\n12.2.2 target标记 283\r\n12.2.3 path标记 284\r\n12.2.4 filter标记 285\r\n12.2.5 tstamp标记 285\r\n12.3 Ant关键任务 286\r\n12.3.1 Ant任务 286\r\n12.3.2 antcall任务 286\r\n12.3.3 available任务 287\r\n12.3.4 echo任务 288\r\n12.3.5 fail任务 288\r\n12.3.6 property任务 288\r\n12.3.7 taskdef任务 289\r\n12.4 目录和文件系统任务 289\r\n12.4.1 chmod任务 289\r\n12.4.2 copy任务 290\r\n12.4.3 delete任务 290\r\n12.4.4 mkdir任务 291\r\n12.4.5 move任务 291\r\n12.4.6 touch任务 292\r\n12.5 外部执行任务 292\r\n12.5.1 apply任务 292\r\n12.5.2 exec任务 293\r\n12.5.3 execon任务 294\r\n12.5.4 java 任务 295\r\n12.5.5 javac任务 295\r\n12.5.6 sql任务 296\r\n12.6 文件库任务 298\r\n12.6.1 jar任务 298\r\n12.6.2 war任务 298\r\n12.6.3 zip任务 299\r\n12.6.4 unjar. unzip和unwar任务 300\r\n12.7 文件读写和控制 300\r\n12.7.1 replace任务 300\r\n12.7.2 mail任务 301\r\n12.8 源代码控制任务 301\r\n12.8.1 cvs任务 301\r\n12.8.2 get任务 302\r\n12.9 一些关键的可选任务 302\r\n12.9.1 junit任务 302\r\n12.9.2 junitReport任务 303\r\n12.10 常用的嵌套参数和元素 304\r\n12.10.1 classpath元素 304\r\n12.10.2 mapper元素 305\r\n12.10.3 fileset元素 306\r\n12.10.4 patternset元素 306\r\n12.10.5 zipfileset元素 306\r\n第13章 Ant API 参考 308\r\n13.1 概述 308\r\n13.2 org.apache.tools.ant包 309\r\n13.2.1 AntClassLoader类 309\r\n13.2.2 BuildEvent类 310\r\n13.2.3 BuildException类 310\r\n13.2.4 BuildListener接口 311\r\n13.2.5 BuildLogger接口 312\r\n13.2.6 DefaultLogger类 313\r\n13.2.7 DesirableFilter类 314\r\n13.2.8 DirectoryScanner类 314\r\n13.2.9 FileScanner接口 316\r\n13.2.10 IntrospectionHelper类 317\r\n13.2.11 Location类 318\r\n13.2.12 Main类 319\r\n13.2.13 PathTokenizer类 319\r\n13.2.14 Project类 319\r\n13.2.15 ProjectHelper类 323\r\n13.2.16 RuntimeConfigurable类 324\r\n13.2.17 Target类 324\r\n13.2.18 Task类 326\r\n13.2.19 TaskAdapter类 327\r\n13.2.20 UnknownElement类 328\r\n13.2.21 XmlLogger类 328\r\n13.3 小结 329\r\n第14章 JUnit API参考 331\r\n14.1 junit.framework包 331\r\n14.1.1 Assert类 331\r\n14.1.2 Protectable接口 334\r\n14.1.3 Test接口 335\r\n14.1.4 TestCase类 335\r\n14.1.5 TestListener接口 339\r\n14.1.6 TestFailure类 339\r\n14.1.7 TestResult类 340\r\n14.1.8 TestSuite类 342\r\n14.2 junit.extensions包 344\r\n14.2.1 ActiveTestSuite类 344\r\n14.2.2 ExceptionTestCase类 345\r\n14.2.3 RepeatedTest类 346\r\n14.2.4 TestDecorator类 347\r\n14.2.5 TestSetup类 348\r\n第15章 Cactus API参考 350\r\n15.1 概述 350\r\n15.2 org.apache.cactus包 350\r\n15.2.1 AbstractTestCase类 350\r\n15.2.2 Cookie类 353\r\n15.2.3 FilterTestCase类 357\r\n15.2.4 JspTestCase类 358\r\n15.2.5 ServiceDefinition类 359\r\n15.2.6 ServiceEnumeration类 359\r\n15.2.7 ServletTestCase类 359\r\n15.2.8 ServletTestRequest类 不推荐 362\r\n15.2.9 ServletURL类 363\r\n15.2.10 WebRequest类 365\r\n15.2.11 WebResponse类 367\r\n15.2.12 WebTestResult类 368\r\n15.3 org.apache.cactus.util包 369\r\n15.3.1 AssertUtils类 不推荐 369\r\n15.3.2 ClientCookie类 不推荐 370\r\n15.4 org.apache.cactus.server包 371\r\n15.4.1 FilterConfigWrapper类 371\r\n15.4.2 HttpServletRequestWrapper类 372\r\n15.4.3 PageContextWrapper类 373\r\n15.4.4 RequestDispatcherWrapper类 373\r\n15.4.5 ServletConfigWrapper类 373\r\n15.4.6 ServletContextWrapper类 374\r\n第16章 HttpUnit API参考 375\r\n16.1 概述 375\r\n16.2 com.meterware.httpunit包 375\r\n16.2.1 AuthorizationRequired-\r\nException类 375\r\n16.2.2 GetMethodWebRequest类 376\r\n16.2.3 HTMLSegment接口 376\r\n16.2.4 HttpException类 379\r\n16.2.5 HttpInternalErrorException类 379\r\n16.2.6 HttpNotFoundException类 380\r\n16.2.7 HttpUnitOptions类 380\r\n16.2.8 HttpUnitUtils类 381\r\n16.2.9 IllegalRequestParameterException类 382\r\n16.2.10 MessageBodyWebRequest类 383\r\n16.2.11 PostMethodWebRequest类 383\r\n16.2.12 PutMethodWebRequest类 384\r\n16.2.13 SubmitButton类 385\r\n16.2.14 TableCell类 385\r\n16.2.15 WebClient类 386\r\n16.2.16 WebConversation类 389\r\n16.2.17 WebForm类 390\r\n16.2.18 WebLink类 392\r\n16.2.19 WebRequest类 393\r\n16.2.20 WebResponse类 397\r\n16.2.21 WebTable类 400\r\n第17章 JUnitPerf API参考 402\r\n17.1 com.clarkware.junitperf包 402\r\n17.1.1 ConstantTimer类 402\r\n17.1.2 LoadTest类 402\r\n17.1.3 RandomTimer类 406\r\n17.1.4 ThreadBarrier类 406\r\n17.1.5 ThreadedTest类 407\r\n17.1.6 ThreadedTestGroup类 407\r\n17.1.7 TimedTest类 408\r\n17.1.8 Timer接口 410
本书讲述了在极限编程的实践中如何使用开源工具实现自动测试和持续整合的技术.
让我们把这句话展开来说.自动测试和持续整合是极限编程软件开发方法中12个核心实践中的两个.极限编程是一种轻量级的软件开发过程,其关键价值是反馈.交流.简单和勇气.在第1章中我们将会说明完整的极限编程的实践过程,目前能说的是极限编程是由“通用的开发实践”和“正确的方法”所组成的.
在通用的开发实践中包含测试和不断整合的工作.几乎没有哪个软件开发商在开发中放弃这些步骤,毕竟一个系统需要经过整合才能发布,并需要通过测试确保用户能够接受开发出来的产品.由于互联网业的萧条,使得大量不遵守这些实践原则的软件开发商退出了这一行业.但是还是有许多软件开发商要么抗拒这些做法,要么即使他们承认必须这样做,却抱怨“忙都忙不过来了,哪有时间做这些事情.”本书正是解释和阐述了软件工具的妙用,以帮助读者将这些有价值的开发实践应用于软件开发中.
为什么要在工具的使用上花费大量的时间
极限编程是一个以人为中心的开发哲学,所以如果我们将注意力集中在工具上是极具讽刺意味的.极限编程方法认为软件编写过程中的关键挑战是来自于人的挑战—例如将人聚集在一起工作,帮助程序员学习和控制情感.它的四个关键价值(交流,反馈,简单,勇气)都以人为本.至今为止,大多数关于极限编程的书籍将重心放在人身上:这些书都在概括和传播极限编程的哲学及思想体系(Kent Beck认为《Extreme Programming Explained》可以作为极限编程的宣言),同时这些书中还说明编写软件的感觉.通过这些努力,Kent Beck与极限编程的发明者们遵从他们自己的哲学:先解决最重要的问题.但是,现有的书并没有覆盖所有实践极限编程方法的技术细节,这正是这本书产生的原因.
我们将解释在Java环境下(尤其是在J2EE环境下,尽管大多数工具在任何环境下使用起来都差不多)怎样实现持续整合和自动测试.我们将着眼于某些技术细节并提供有关范例以演示工具的使用.我们还将为大家讲述如何使用JUnit.Cactus.HttpUnit.JUnitPerf和JMeter编写自动测试,如何使用Ant(与前面所列的工具结合使用)来实现持续整合.
谁应该阅读这本书
尽管本书的观点来自极限编程,但你却没有必要通过实践极限编程才能从本书中获益.任何一个希望在自动化测试和持续整合方面得到帮助的人都能从这些工具和范例的实践中受益.如果你对极限编程一无所知,你应该参考第1章以便理解前言的其余部分,这样你才能够了解本书中某些做法的意图所在.实际上,前言部分介绍了自动测试和持续整合对于所有程序员的价值.本书假定你是一名有一定经验的Java程序员.因为书中讲述了应用程序的测试和J2EE平台下的整合工具,本书还希望读者通晓J2EE技术和拥有开发经验.那些对J2EE应用程序开发不感兴趣的人也能从中找到大量有价值的材料,因为任何一个Java(JMeter与HttpUnit甚至不仅仅局限于Java项目)软件项目都能够应用书中的大部分工具.如果程序员不熟悉J2EE但又想将这些技术和工具应用于J2EE应用程序中的话,那我推荐你阅读一本全面介绍J2EE的书籍—《Developing Java Enterprise Applications, 2nd edition》,作者为Stephen Asbury与Scott Weiner.
为什么要开源
我们很难对开源运动在软件领域的发展视而不见.当然,开源此时是一个“强意词”,但是开放源代码的开发工具较传统的工具提供了更加引人注目的优势—尤其在极限编程的开发中.我们将优势归结为两点:第一,开源工具很实用.第二,开源与极限编程有着相近的哲学思想.
开源工具的优势:
价格合理.开源软件几乎都遵守免费原则,本书中的所有工具我们都能通过Internet免费获得.免费软件意味着你或者你的公司不必破费就能使用它们,这总是有好处的,但是在这里不是主要的.主要的好处是你采用的这些工具不会受到你的老板或管理者的阻拦,因为他们担心这些软件只是一时流行.比如说,一旦你下载了JUnit,你喜欢上了它并且在团队内极力推广—加速软件开发和改进了质量—绝对没有人会阻止你的行为.如果在开始采用极限编程之前,你必须支付7500美元购买相关部署软件,这一定会遭到质疑的,不是吗?
工具是高质量的.程序员们每天都使用开源的开发工具.因为改进工具意味着直接改进他们的工作环境,所以开源开发工具不断接受改进和进行bug的修复.这项工作快速且持续不断地进行着.
工具符合标准.本书中涉及的工具,尤其是JUnit和Ant,在它们所处的领域都是标准.无数的开源项目使用Ant,而JUnit(一些工具基于它)的作者为Kent Beck(极限编程之父)与Erich Gamma(《Design Patterns: Elements of Reusable Object-Oriented Software》的主要作者之一).
极限编程与开源软件的配合
极限编程与开源的发展是紧密联系的.两者都鼓励开放,给出一种合作开发的方式—如果你愿意,也可以采用这样的方法参与其中.两者的哲学都承认人类的弱点—世界上不存在完美的代码,并赞同帮助他人查找和修复错误的人应该得到大家的感谢.所有的开源代码都是共享的(这正是极限编程强调的).许多开源项目使用自动测试,并从中获益.使用自动测试对开源项目来说尤其重要,因为代码的来源广泛,而且必须进行整合.两个系统都要求进行小型且增量式的软件发布.当然,两者的哲学同样重视代码—开源以阅读代码是愉快的.受教育的和有帮助的这一条件为前提.
它们两者的共通点全部罗列出来的话要花上不少功夫.通过使用开源工具(以反馈.协助以及代码的形式将意见转达给开源社团)即等于实现了开源的一些价值或做法,这些价值和做法正可以将极限编程发扬光大.
阅读源代码
如果你在寻找关于工具的更多信息,最好的地方就是它们的源代码.除了工具包含的Javadoc文档(另一份唾手可及的参考资料)以外,源代码便是工具行为最权威的发言人.开源软件存在的原因之一(除了人们喜欢免费这一因素之外)就是程序员可以深入研究软件开发者的作品.通过仔细阅读源代码,你能深刻了解程序是怎样实现这些工作的,并且领略编程本身的艺术魅力.如果你在使用工具时不幸遇到bug,那么阅读源代码就能帮助你确定bug到底在何处.
自动测试:概述
极限编程注重测试,将测试作为软件开发流程的核心.在《Extreme Programming Installed》后记中,Dan Rawsthorne说:“极限编程之所以成功,在于它以产品的品质为中心,而不是以最后发布的软件产品为中心.”通过不断对软件进行测试,验证软件是否正常工作和软件是否符合客户的需求.自动测试确保测试是连续进行的.如果没有测试,一个团队只能靠猜测来判断软件是否符合需求.在缺少自动测试的情况下极限编程不能执行,开发也不可能完成任务.所有的软件项目都需要使潜在的用户满意并且没有错误.
测试和重构
重构(将已有的代码变得更加简单.明了,或添加功能)是另一个极限编程核心实践.重构是不能脱离测试进行的.如果你不采用极限编程,那么你不可能坚持重构.尽管大多数稳定或者难以改动的项目只需要偶尔的修改,但为了能够正确地修改系统,程序员必须修改现有的设计.此时自动测试将发挥其应有的作用.
面向对象程序设计(在某种程度上也包括其他的程序设计风格)将接口与实现分离.理论上,这意味着你可以改变类或方法的内部逻辑,而不必同时修改与之相关的代码.已经有很多书讨论了这种强大的抽象技术.但是,如果在实践中,程序员由于害怕干扰与接口互动的代码而不敢改变底层逻辑,那么将接口与实现分离实在没有任何意义.广泛的测试(频繁地运行测试)可以确保系统按照正常的方式运行,并且允许自由修改内部的逻辑.任何因变动所导致的问题都会被测试捕捉到.如果测试的时候设计A与设计B产生同样的结果,那么这两个设计便可以互换.有了测试,程序员便能放心对代码进行重构,因为测试可以保证代码的正确运行.
自动测试的种类
在极限编程中单元测试(unit test)是最常被提及的测试,但这只是众多测试中的一种.单元测试需要与整合测试(integration test).功能测试(functional test),及一些其他的辅助测试(auxiliary test)—如性能测试(performance test)及回归测试(regression test)等等共同合作,以确保整个系统的完全运行.
1.单元测试:JUnit
单元测试是极限编程实践中的第一线测试(同时可能是最关键的测试).编写一个单元测试,就是取出一个“单元”的程序代码,并测试任何可能产生的错误.单元测试,通常会检验类中所有公共接口中的方法.优秀的单元测试,无需测试类中所有行为组合,也无需测试某些极其简单的方法(如Get与Set方法).更确切的说,单元测试提供了一个重要的常识,即确保单元代码的行为是我们预期的.有了这项工作作为保证,公共接口才变得有意义.这种方式可以使改变单元中的行为更加容易,同时也提供了一个单元行为的简便(并且是可以验证的)的向导.并且程序员能通过对测试的讨论发现类或方法的使用目的.
在极限编程中,单元测试是每天编码周期的一部分.理想的方式是先写测试再写程序代码,并且在具体实现中将测试作为向导.本书两位作者都是以这种方式设计程序的.同时我们发现,如果没有单元测试的向导与纠正,我们几乎无法开展工作.当完成一个单元测试,开发团队便将测试加入项目的测试套件中.这个测试套件每天都要执行好几次,并且所有的测试必须都要通过,单元测试100%的通过率比以下替换办法更合理:那就是有一部分关键的代码不工作.(如果这些程序代码不是关键的,那它为什么会在项目中出现?)
要建立一个高品质的系统,必须确保每一个类的正常运行,因为只有这样才能保证整个系统的运行无误.单元测试同时使程序代码架构变得简明易懂.如果一个软件程序员在同一段程序代码中的不同地方编写三次测试,人的惰性会促使他将这些代码移至另一个相同的地方.
JUnit是一种轻量级的测试框架.JUnit的作者Kent Beck和Erich Gamma依据SUnit设计出JUnit,SUnit是一个应用于Smalltalk并且取得成功又广为大家欢迎的测试框架.由于这个框架非常简单,因此其本身不断地被采用和扩充.本书介绍的所有测试工具都可以与JUnit互动或扩充自JUnit框架(除了JMeter,因为它是一个GUI工具).
2.整合/容器内的测试:Cactus
假设单元测试涵盖对象X,那么相关的对象Y与Z怎么处理呢?这三者共同组成子系统A.单元测试被假设是具有独立性的.一个好的单元测试,是无论系统是多么的混乱,它仅仅确认它所测试的类的功能是否与期望达成一致.有许多论文讨论到如何避免单元测试依赖性的方法.(这些论文可以在http://www.junit.org 网站中找到,这些论文的核心思想是提供一个与测试类相关联对象的模拟实现.)不管如何,单元测试还是应该尽可能的独立.
在《Extreme Programming Installed》一书中,Jeffries等人对于只出现于类间合作中的错误做了一个有意义的观察.他们说:“以我们的实践经验告诉我们,这类错误很少发生.据我们猜测,或许是由于我们先编写了有关测试,所以避免了这类错误的产生.”他们也承认,“当显示出这种错误信息时,这样的问题确实很难解决.”一个好的单元测试,确实应该捕捉到大多数的错误.而系统整体的行为,则是由验收测试(acceptance testing)(也称为功能测试)来检验.但是,好的单元测试也应该检查子系统的行为.整合测试(integration testing)则负责单元测试与验收测试之间的灰色地带,整合测试提供的是一种完整的测试,它测试所有程序的合作和准确定位介于期望结果与实际结果之间微妙的差异.整合测试一般都没法达到百分之百(例如,可能是相关的类尚未完成),但成功率一般还是相当高的(介于80%至90%之间).
整合测试中有一个重要的变化形态,就是容器内测试(in-container test).在J2EE开发模型中规定组件必须存在于容器中.这些组件依赖容器所提供的服务.因此组件与这些服务的交互必须有相应的检查.尽管有些互动关系可以成功地模拟出来,但是模拟J2EE容器提供的所有服务是非常耗时间的,同时对于此类行为的检查也不是那么完美.由于容器的实现不同,所以有些服务,例如由部署描述符指定的行为就很难被测试到.
Cactus框架提供了存取J2EE Web容器的能力(也可以存取其他类型的容器,如EJB容器).因为它允许测试容器内的程序代码,Cactus在某种程度上减轻了开发人员进行扩展的负担,以及降低了实现模拟行为的难度(也可以使用真正的服务来代替).由于是在一个由Cactus所模拟的环境中执行程序,并且这个环境十分贴近真实的运行环境,因此采取这样的方法提供了一个可评估的反馈.当单一的对象只是与容器的服务互动时,容器内测试就犹如快速直接的单元测试.
3.验收/功能测试:HttpUnit
功能测试则确保整个系统是按照预期的行为运行的,这个测试也称为验收测试.因为这个测试是客户用来检验系统是否已经完成(换句话说,一个Web站点是否完成,取决于其是否可以让用户登录,显示产品,以及线上订货).有时功能测试会令人怯步(因为功能测试不像单元测试,能立即对生产力有所帮助).但功能测试对于衡量进度,及找出缺陷都非常重要,缺陷通常来自过去未通过的测试,或者是未完成的功能模块.因为验收是客户的职责,所以验收测试是由客户编写的(由程序员实现).单元测试是由开发团队负责,例如测试Foo类是否能够正确运行.而验收测试则是由客户负责(客户可能不知道Foo类),他们测试整个系统是否能够正确工作.
验收测试并不非常依赖特定的实现,例如,在重构期间,开发团队可能决定不再需要SubCategory对象,此时SubCategory便无法正常运行.因此他们修改整合测试(如果需要的话)以符合新的系统结构.但是功能测试没有改变,因为用户仍然需要目录搜索功能.
功能测试并不需要总是100%地运行,但应该在软件发布之前进行.功能测试通常验证特定的故事(顾客需求的特性在极限编程中的表述).同样地,它们也可以在开发周期中追踪进度.每一个运行的测试代表了一种完成的特性.
很遗憾但可以理解的是,没有人已经编写了一个通用的验收测试工具.JUnit只能测试任意的Java类,但验收测试的工具必须是为特定应用程序定制的.对一个以数字为主的程序,验收测试只要确认输入.输出即可.但对于一个资料输入的应用程序,却需要使用带有图形界面的资料输入和输出工具.
我们选择HttpUnit来做功能测试,HttpUnit是用作测试的API,它能够以程序的方式调用Web资源,并且检验回应的结果.它的框架与JUnit结合,可以显示一个HTML网页的内在结构,让你很容易检查其结构化的元素(show_product.jsp的响应的是一个含有产品价格的表格).这种方式很符合本书的要求,因为J2EE最重视的就是Web组件.验收测试J2EE中具体的组件可能并不需要特定的框架,因为特定的框架包含低级的逻辑表达.
4.性能测试:JUnitPerf及JMeter
除了基本的测试功能外,还有许多种测试:如平行测试(parallel test,测试新系统是否与旧系统的执行结果一致).性能测试(performance test).验证测试(validation test,对于不正确的输入,系统给出相应的回应)等等.所有这些测试当中,性能测试可能是应用最广泛的.毕竟,即使是功能最强大的系统,如果使用者不愿意使用,也不值一毛钱.客户端的应用程序性能比较差,这是一个问题,服务器端应用程序慢慢吞吞,更是一种危机.J2EE的应用程序一般是在服务器端,每分钟处理成百上千个交易.因此即使只是一小部分的代码没有效率,也可能拖垮整个系统.因此在这样的环境中,运行性能测试的重要性更胜于功能测试(甚至是单元测试).
我们没有提供有关研究性能的工具(可以解决性能问题的关键工具).而我们提供的是可以在早期发现性能问题的测试工具.JUnitPerf可以执行单元的性能测试—它可以装饰(decorate)现有的JUnit测试,当执行时间超过期望的时间时,表示这个性能测试失败.这种测试支持重构,它能评估可能存在问题的程序是否达到指定要求.JMeter提供强大的性能测试—当执行远程服务器请求的时候(Web.EJB.数据库等),它可以测量系统的响应时间,并以图形显示出来.使用JMeter,客户可以编写验收测试.例如,在Web服务器中,当150个使用者同时访问时,可以保持3秒钟以内的响应时间.
持续整合:概述
持续整合(continuous integration)是极限编程中另一项实践,受益于使用优秀的工具.基本上,持续整合意味着每天构建数次目前已完成的系统(包括通过所有的测试套件).只要你持续地进行整合操作,可以说你的系统随时可以走出大门(至少已完成的部分都是没问题的).这个过程必须实现适当的自动化(一个简单的指令,或许是从一个计时器发出便能构建及测试整个系统),否则一定没有人愿意做这样的事情.有许多合理的理由使我们花费时间和精力来建立持续整合的机制,例如:客户和开发团队可以同时了解实际的进度,减少系统整合的bug并且频繁地运行测试.
可视化进度
从实现持续性整合中得到的某些“微妙”效果将超过它所实际带来的利益.这将肯定团队开发工作的成果.完成构建并且通过测试系统,代表所有开发人员付出努力的结晶.身为系统开发人员,你意识到只要自己提交已完成的代码,自己便会融入开发的流程中.你不会莽撞的朝你自己的方向走,因为你知道你的代码将融合到整个系统中.任何人可以随时看到系统工作的状态,当通过更多的验收测试,并且系统展现出新的功能时,程序员感受到进度朝目标方向前进,进而从最终的成果中得到信心(如果完成的系统毫无生气,也没通过验收测试,这意味着这个系统存在重大的麻烦,这一般都是因为系统太久没有进行整合所造成的).
减少整合的痛苦
如果X类并不和Y类一致,尽快知道这个问题是很有意义的.X类和Y类在发生冲突之前的时间越长,当发现X和Y冲突问题的那一天,作者的记忆早已在这个问题上模糊.持续整合能在很短时间内保证矛盾的双方相遇.它不仅仅是立即解决这些问题(这个缺陷一般是由于矛盾的变动导致的),而且它强制开发人员进行有关检查以找出产生问题的原因:“Ah,明白了,Sandra已经重构了CapriciousDictator类,因此我就没有理由为OppressiveRegime再增加extraEnforcement方法了.”这样,一个“纯净”的系统诞生了.
经常运行测试
单元测试是完整的持续整合系统的一部分.所有的单元测试百分之百通过之后,并且功能和整合测试运行过后完整的构建任务才完成.这不仅仅是产生系统,同时也证实系统能正确地运行.如果不经常运行测试,测试将失去价值.如果你经常进行构建运行工作并且以同样的频率运行测试,那么将对系统性能提供稳定的升级.
重新启动
我的第一个大型软件项目是比较混乱的……但却进行了持续整合的工作.你可能想知道为什么.我们的团队,由一群没有经验的Java开发人员组成,无法在任何机器上得到一个稳定的构建系统.我们只好在不可能的最后期限的压力下连接一大堆项目的附属文件和库文件.最后我们只能将当前在整合服务器上的所有代码通过一个基于外壳的建造脚本来完成编译,因为没有人有能力编译各自机器上的本地文件.为了看到某些部分在工作,我们将其上传到整合服务器中.这样,我们5到6个程序员同时工作,每次需要五分钟来运行我们的脚本文件(包括重新启动Web服务器).如果开发人员希望去整合文件就需要在重新启动Web服务器之前得到许可:“没有人反对我重起Web服务吧?!”结果是混乱的—但如果有人做了一些改变而导致冲突,我们将瞬间知道.
持续整合和J2EE
基于J2EE的开发模型,在整合和部署阶段,应用程序经过了具有重要意义的自定义过程.对一个完全的J2EE应用程序进行整合和打包需要深入了解不同的档案格式(JAR.WAR 和 EAR),部署描述符和应用程序部署方式,组件嵌入已有容器等相关知识.由于J2EE程序的构建过程十分复杂,我们需要一个自动化构建工具.
Ant辅助支援工作
与其他极限编程实践所不同的是,实现持续整合主要是通过技术手段.一旦拥有了一个自动化过程,应用它将会是相当简单和极具竞争力的,开发人员在喘息之间就能将任何妖魔鬼怪玩弄股掌之间.你所需要做的仅仅是借助工具自动化进行循环构建工作来开始你的持续整合.因此,本书将涉及Ant,一个脱颖而出的Java构建自动化标准.
Ant允许开发人员编写XML形式的测试构建脚本来调用Java类实现相关工作.此工具是跨平台的(针对基于Java的工具的严格标准)并易于扩展和修改.Ant执行构建工具的所有基本任务:编译.档案的建立.classpath(类路径)的管理等等.它也支持测试和自动生成报告,还支持很多有用的任务,例如可以和源代码控制进行交互,FTP操作和对属性文件的编辑工作.这个预定的构建任务“兵器库”工具允许开发人员方便地将复杂的构建过程自动化.经过Ant的些许工作,就能够从代码控制中取出.构建.定制符合所需环境(例如产品相对整合)的Java应用程序,测试并且通过一个简单命令将Java应用程序部署到远程服务器上.
本书的结构
本书分为三个部分:
第一部分:技术介绍和关键概念.这一部分以极限编程(Extreme Programming)方法学概览开始—这是一个简短的初学者课程或者看作是实践者的复习.本书的该部分概览可以使你明白本书涉及到的实践(自动化测试和持续整合)如何融入到极限编程的逻辑图中.第2章解释了J2EE的构建和部署模型,特别介绍了需要一个自动化构建工具来帮助完成这一过程.在这部分结尾部分,还为大家介绍我们在本书中将构建,测试和重构多次的一个范例应用程序.
第二部分:掌握相关工具.第二部分的每一个章节都为开发人员预备了一个特殊工具的介绍.第4~6章则涵盖了通过Ant来实现持续整合,其余每一章节都介绍一个自动化测试工具.这个指南只用范例代码来举例说明如何运行工具来构造.测试.部署和重构你的代码.在第二部分结尾,你就能综合使用这些工具开展工作了.
第三部分:API(应用程序接口)和标记参考(Tag Reference).该参考部分深入剖析了本书覆盖范围的API的详细信息.所有的类,变量和需要使用到有关API的方法(或标记及其属性,标记的介绍主要在第12章),这些内容以标准Javadoc风格和相关的代码范例一起呈现给你.
在站点上你能找到的内容
所有的配置脚本.构建脚本.应用程序和本书中涉及的其他源代码将在线提供于http:// www.wiley.com/compbooks/hightower .同时还有范例源代码和安装指令勘误表(对于发布紧凑的开源API尤其有必要)和其他后继信息.这个系列的其他书籍的信息你可以在http://www.wiley.com/compbooks 获得.