轻量级且运行很快的单元测试可以被迅速的执行完,并且在测试失败时能够给出很快的反馈。而另一方面,如果缓慢运行的集成测试与单元测试混在一起,那么单元测试将要花上更多的时间来执行,结果开发人员也要等更久才会得到关于单元测试失败的消息。解决之道就是为单元测试和集成/性能测试创建单独的构建计划。使用这种方式后,如果单元测试构建计划失败了,由于其执行速度很快,开发人员不再需要等待很长时间才被通知到构建失败。如果单元测试成功后,集成测试和性能测试计划才会开始。
另外,还有一个补充方法——可以使用分布式构建。例如,如果你的web功能测试由于要在几个不同的浏览器上运行并因此消耗很长时间,那么可以为每一个浏览器设立一个构建作业,以让它们(可能在不同的机器上)并行运行。
另一个问题来自过于缓慢和效率低下的测试用例。有许多方法可以用来监测这些缓慢的测试。提高测试上的运行时间意味着有些测试会由于所需时间太长而无法运行。这也许是因为它们被设计的很糟糕,也许是因为性能问题(需要进步一深究),抑或是集成测试伪装成了单元测试。
密切关注代码质量
持续集成服务器不应当仅仅是一个自动构建的机器。它应当成为你团队中沟通的中枢,尤其在代码质量上。时刻关注编码规范和一些度量值如代码覆盖率以及代码复杂度,它们可以让应用程序更加可靠且更易维护。
有很多优秀的工具可以帮助维护应用程序中良好规范的代码。静态分析工具如Checkstyle,PMD和FindBugs,它们根据代码标准、最佳实践或者潜在的bug来对代码进行分析。你所有使用的工具如何配置要依赖于你想要达到的目标。例如,Checkstyle更多关注于编码规范和最佳实践,而 Findbugs则更倾向于找出错误的,破碎的或者危险的代码。所有的这些工具可以很轻松地集成到自动化构建过程中,并且可以和Ant、Maven一起很好地工作。
覆盖测试率是代码质量的另一个方面。它用来衡量测试执行时所访问的代码行数。Java开发人员中对覆盖测试率统计的相对值仍然有着分歧。事实上,虽然它可以告诉你应用程序中的哪些行被执行,但是没法知道那些测试是写得很彻底、写得很好,抑或仅仅是简单的走马观花。总而言之,测试覆盖率不能保证你的测试质量很高——只有人工进行代码检查时才能确保如此。然而覆盖测试率的度量值可以很好地展示出哪些代码没有被测试过。在Java世界里,用得最多的代码覆盖率测试工具当属Clover和Cobertura,前者是一个非常强大的商业代码覆盖率测试工具,而后者是一个更加轻量级的开源工具。它们都可以很容易地集成到基于Ant和Maven的构建脚本中。
编码规范也可以作为非常有效的培训支持和指导活动,尤其对于那些经验不足的开发人员。CI工具可以提供这些数据如何随着时间的推移而变化的高层次图片,还可以关注开发人员在应用他们学到的技巧时做得有多好。例如,如果一个类只有很低的代码覆盖率,甚至没有,就意味着某个新的开发人员在消化吸收小组培训的测试驱动开发和测试实践上出现了问题。这种方法还可以通过代码审查和定期的代码质量会议(讨论任何新问题或者动向的会议)完成。
一旦构建结束——自动化部署过程
构建应用程序只是开发生命周期中的一部分。一旦代码编译测试后,需要进行其他的活动,例如部署到阶段性(staging)环境、冒烟测试、功能测试和性能测试、准备发布说明和提醒QA人员最新的发布。
将最新的构建结果自动部署到集成服务器上是一件相对简单的事情。而将其部署到阶段性环境或者生产环境下,则需要涉及一些与常规构建作业不一样的工作。一般而言你需要一个更加严格,更加正规且有更多可跟踪性和问责的过程。它通常涉及到的任务如下:
* 为阶段发布标记源代码
* 编译测试应用程序
* 发布构建产品
* 将应用程序部署到阶段性环境中
* 运行数据库更新脚本或者其他特定环境脚本
* 运行冒烟,功能和性能测试
* 准备并发布产品说明
* 提醒关于最新阶段发布的相关利益人
这通常是一个手工任务,但是其中的大部分工作没有理由不能自动化。事实上,开发生命周期中的自动化包装,部署和发布具有很稳固的商业意义。一方面自动化能够得到更加可靠的构建:计算机不会忘记部署过程中的某一步,也不会在发生测试失败后继续进行。它还能够节约开发人员的时间:阶段性发布由之前几小时的 shell脚本编程变为了只要点击一下按钮。它比以前的速度要更快,并且可以在没有人的情况下完成工作(例如,通宵或者午休时间)。
像Maven 2这样的工具也能够帮助自动化一些步骤。Maven Release插件使得Maven的用户能够自动化处理一些如“更新版本号”,“Subversion中新增标签”,以及“向Maven存储库中发布构建产品”的工作。它可以用来管理阶段构建,并决定在不同的环境部署不同的发布产品。尽管如此,一旦产品构建结束并且可以部署到阶段性环境者生产环境时,这个过程就会变得更复杂。
千真万确,现实世界中的部署步骤数目经常要比简单的用一个WAR文件多。根据应用程序架构和产品平台,你可能需要在阶段性环境者生产环境下的数据库中运行SQL更新脚本、用一个专用的工具部署web服务、运行自动化的冒烟测试或者做一定量服务端的工作。
CI可以帮助简化比这些还要复杂的步骤。例如通过分布式构建,你可以设立阶段性环境或生产环境上的构建代理,并在该机器上直接运行相应的任务。几乎所有的 现代CI工具都支持相当好的安全模型,目的是为了将应用和产品环境限制给一些特定的人,以及跟踪谁在什么时间运行了什么构建。
这是CI的一个相对较新的应用,不同的工具在处理应用程序部署时使用的方法也不一样。有一些,如Hudson,允许在构建作业中定义多个步骤,只有当前一 个步骤成功后,才能执行后续步骤。其他的像Cruise和Anthill Pro,都尝试将部署生命周期中的如阶段和生产环境直接集成到构建工具中,尽管有时候这样会带来额外的复杂度开销。
还有更多低层次的操作可以和CI服务器联合起来使用。一个选择是使用诸如Ant或者Maven的构建工具。Ant对于这种类型的脚本特别灵活。另一个流行的选择是古老的Makefile,或者Unix上的shell脚本。它们的缺点是操作系统相关的,并且对于那些不熟悉shell脚本精髓的Java 开发人员来说很难掌握。想要与Java更加友好,可以选择动态语言诸如Groovy或者Gant(一个使用Groovy而不是XML来制作Ant脚本的工具)。 Groovy在提供所有轻量级的动态脚本语言中所有的优点的同时,也保留了对Java开发人员的熟悉程度和可读性。