《持续交付:发布可靠软件的系统方法》第3章 持续集成

2019-09-28 13:03:32 浏览数 (1)

第3章 持续集成

3.1 引言

  • 持续集成要求每当有人提交代码时,就对整个应用进行构建,并对其执行全面的自动化测试集合。而且至关重要的是,假如构建或测试过程失败,开发团队就要停下手中的工作,立即修复它。持续集成的目标是让正在开发的软件一直处于可工作状态
  • 持续集成是一种根本的颠覆。如果没有持续集成,你开发的软件将一直处于无法运行状态,直至(通常是测试或集成阶段)有人来验证它能否工作。有了持续集成以后,软件在每次修改之后都会被证明是可以工作的(假如有足够全面的自动化测试集合的话)。即便它被破坏了,你也很快就能知道,并可以立即修复

3.2 实现持续集成

3.2.1 准备工作

  • 在开始做持续集成之前,你需要做三件事情
版本控制
  • 与项目相关的所有内容都必须提交到一个版本控制库中,包括产品代码、测试代码、数据库脚本、构建与部署脚本,以及所有用于创建、安装、运行和测试该应用程序的东西
自动化构建
  • 你要能在命令行中启动构建过程。必须满足如下条件:人和计算机都能通过命令行自动执行应用的构建、测试以及部署过程
团队共识
  • 持续集成不是一种工具,而是一种实践。它需要开发团队能够给予一定的投入并遵守一些准则,需要每个人都能以小步增量的方式频繁地将修改后的代码提交到主干上,并一致认同“修复破坏应用程序的任意修改是最高优先级的任务”

3.2.2 一个基本的持续集成系统

  • 为了做持续集成,你不一定就需要一个持续集成软件,正如我们所说,它是实践,并不是工具
  • 有几个开源工具可供选择,比如Hudson和受人尊敬的CruiseControl家族(CruiseControl、CruiseControl.NET和CruiseControl.rb)
  • 还有两种商业化持续集成服务器为小团队提供了免费版本,它们是ThoughtWorks Studios开发的Go以及JetBrains的TeamCity。其他流行的商业化持续集成服务器还包括Atlassian的Bamboo和Zutubi的Pulse。高端的发布管理以及构建加速系统还有UrbanCode的AntHillPro、ElectricCloud的ElectricCommander,以及IBM的BuildForge,它们都可以用于简单的持续集成
  • 一旦准备好要提交最新修改代码时,请遵循如下步骤
    • (1) 查看一下是否有构建正在运行。如果有的话,你要等它运行完。如果它失败了,你要与团队中的其他人一起将其修复,然后再提交自己的代码
    • (2) 一旦构建完成且测试全部通过,就从版本控制库中将该版本的代码更新到自己的开发环境上
    • (3) 在自己的开发机上执行构建脚本,运行测试,以确保在你机器上的所有代码都工作正常
    • (4) 如果本地构建成功,就将你的代码提交到版本控制库中
    • (5) 然后等待包含你的这次提交的构建结果
    • (6) 如果这次构建失败了,就停下手中做的事,在自己的开发机上立即修复这个问题,然后再转到步骤(3)
    • (7) 如果这次构建成功,你可以小小地庆祝一下,并开始下一项任务

3.3 持续集成的前提条件

3.3.1 频繁提交

  • 对于持续集成来说,我们最重要的工作就是频繁提交代码到版本控制库。每天至少应该提交几次代码
  • 定期地将代码提交到代码主干上会给我们带来很多其他好处。比如,它使每次的修改都比较小,所以很少会使构建失败。当你做了错事或者走错了路线时,可以轻松地回滚到某个已知的正确版本上
  • 前面我们特意提到过“要提交到主干”。很多项目使用版本控制中的分支技术来进行大型团队的管理。然而,当使用分支时,其实不可能真正地做到持续集成。因为如果你在分支上工作,那么你的代码就没有和其他开发人员的代码进行即时集成

3.3.2 创建全面的自动化测试套件

  • 自动化测试有很多种,其中有三类测试我们会在持续集成构建中使用,它们分别是单元测试、组件测试和验收测试
  • 单元测试用于单独测试应用程序中某些小单元的行为(比如一个方法、一个函数,或一小组方法或函数之间的交互)。它们通常不需要启动整个应用程序就可以执行,而且也不需要连接数据库(如果应用程序需要数据库的话)、文件系统或网络
  • 组件测试用于测试应用程序中几个组件的行为。与单元测试一样,它通常不必启动整个应用程序,但有可能需要连接数据库、访问文件系统或其他外部系统或接口(这些可以使用“桩”,即stub技术)。组件测试的运行时间通常较长
  • 验收测试的目的是验证应用程序是否满足业务需求所定义的验收条件,包括应用程序提供的功能,以及其他特定需求,比如容量、有效性、安全性等。验收测试最好采用将整个应用程序运行于类生产环境的运作方式

3.3.3 保持较短的构建和测试过程

  • 将验收测试按功能块进行分组通常是可取的。这样,当仅修改了系统中的个别功能块时,就可以单独运行影响系统这部分功能的验证测试。很多单元测试框架都提供这样的分组功能
  • 项目由几个模块组成,而每个模块的功能相对独立。此时需要认真考虑如何在版本控制库和持续集成服务器上合理地组织这些模块

3.3.4 管理开发工作区

  • 开发环境的管理是特别重要的。当开发人员刚开始新任务时,应该总是从一个已知正确的状态开始。他们应该能够运行构建、执行自动化测试,以及在其可控的环境上部署其开发的应用程序,通常是在他们自己的开发机上。在本地开发环境上运行应用程序时,应确保所使用的自动化过程与持续集成环境中的一致
  • 达到这一目标的第一个先决条件就是细心的配置管理,其次是对第三方依赖的配置管理,最后就是确保自动化测试(包括冒烟测试)都能够在开发机上运行

3.4 使用持续集成软件

3.4.1 基本操作

  • 持续集成软件包括两个部分。第一部分是一个一直运行的进程,它每隔一定的时间就执行一个简单的工作流程。第二部分就是提供展现这个流程运行结果的视图,通知你构建和测试成功与否,让你可以找到测试报告,拿到生成的安装文件等

3.4.2 铃声和口哨

  • 你还可以在构建过程中对源代码进行一些分析工作,包括分析测试覆盖率、重复代码、是否符合编码标准、圈复杂度,以及其他一些健康指标,并将结果显示在每个构建的总结报告中

3.5 必不可少的实践

  • 持续集成是一种实践,不是一个工具,它的有效性依赖于团队纪律
  • 持续集成系统的目标是,确保软件在任何时候都可以工作

3.5.1 构建失败之后不要提交新代码

  • 持续集成的第一忌就是明知构建已经失败了,还向版本控制库中提交新代码。如果构建失败,开发人员应该尽快找出失败的原因,并修复它

3.5.2 提交前在本地运行所有的提交测试,或者让持续集成服务器完成此事

  • 很多现代持续集成服务器还提供这样一种功能,名字叫做预测试提交(pretested commit),也称为个人构建(personal build)或试飞构建(preflight build)。使用这种特性,就不必自己进行提交,持续集成服务器将拿到你的本地变更,把它放在构建网格中运行提交测试
  • Pulse、TeamCity和 ElectricCommander这三种持续集成服务器都已经提供了这个功能

3.5.3 等提交测试通过后再继续工作

  • 构建失败是持续集成过程中一个平常且预料之中的事情。我们的目标是尽快发现错误,并消灭它们,而不是期待完美和零错误

3.5.4 回家之前,构建必须处于成功状态

  • 我们并不建议你工作到很晚来修复失败的构建,而是希望你有规律地尽早提交代码,给自己足够的时间处理可能出现的问题。或者,你可以第二天再提交。很多有经验的开发人员在下班前一小时内不再提交代码,而是把它作为第二天早上的第一件事情

3.5.5 时刻准备着回滚到前一个版本

  • 如果某次提交失败了,无论采取什么样的行动,最重要的是尽快让一切再次正常运转起来。如果无法快速修复问题,无论什么原因,我们都应该将它回滚到版本控制库中前一个可工作的版本上,

3.5.6 在回滚之前要规定一个修复时间

  • 建立一个团队规则:如果因某次提交而导致构建失败,必须在十分钟之内修复它。如果在十分钟内还没有找到解决方案的话,就将其回滚到版本控制系统中前一个好的版本。如果团队能够忍受,有时候也可以延长一段时间来修复它

3.5.7 不要将失败的测试注释掉

  • 有些开发人员常常为了能够提交代码,而将那些失败的测试注释掉。这种冲动是可以理解的,但却是无法被容忍的一种错误行为

3.5.8 为自己导致的问题负责

3.5.9 测试驱动的开发

  • 只有非常高的单元测试覆盖率才有可能保证快速反馈(这也是持续集成的核心价值)
  • 能够达到完美单元测试覆盖率的唯一方法就是使用测试驱动开发

3.6 推荐的实践

3.6.1 极限编程开发实践

  • 对于任何团队,即使不采用其他实践,只用持续集成也会给项目开发带来很大改善,而若与其他实践相结合的话,它的作用会更大。尤其是,除了测试驱动开发和我们前面讲到的代码集体所有权,你还应该考虑把重构作为高效软件开发的基石

3.6.2 若违背架构原则,就让构建失败

  • 开发人员有时很容易忘记系统架构的一些原则。我们曾经使用过一种手段来解决这个问题,那就是写一些提交时测试,用于证明这些原则没有被破坏

3.6.3 若测试运行变慢,就让构建失败

  • 持续集成需要小步频繁提交。如果提交测试要运行很长时间的话,这种长时间的等待会严重损害团队的生产效率,他们将花费很长的时间等待构建和测试过程完成
  • 为了让开发团队注意到快速测试的重要性,可以这样做:当某个测试运行超过一定时间后,就让这次提交测试失败。我们在上一个项目中使用的这一时间是两秒

3.6.4 若有编译警告或代码风格问题,就让测试失败

  • 我们曾经用过一个比较成功的策略,即只要有编译警告,就让构建失败,但我们的开发团队常常把它叫做“纳粹代码”。这在某些场合可能有点儿苛刻,但作为强迫写好代码的一种实践,还是很有效的
  • 代码质量检查的开源工具,如下所示:
    • Simian是一种可以识别大多数流行语言(包括纯文本)中重复代码的工具
    • JDepend是针对Java的免费版本,它有一个.NET的商业版本NDepend,它们拥有大量对设计质量进行评估的实用(和不太实用)的度量指标
    • CheckStyle可以对“烂代码”做一些检查,比如工具类中的公共构造函数、嵌套的代码块和比较长的代码行。它也能找到缺陷和安全漏洞的一些常见根源。它还很容易被扩展。FxCop是它的.NET版本
    • FindBugs是一个Java软件,它是CheckStyle的替代品,有一些相似的校验功能

3.7 分布式团队

  • 从技术角度上看,最为简单的方法(也是从流程角度上讲最有效的方法)就是使用共享的版本控制系统和持续集成系统

3.7.1 对流程的影响

  • 为了使交付过程更加平稳,让各团队之间的人员做定期轮换也是非常必要的,这样每个地方的成员都能与其他地方的团队成员建立起一些私人交情。对于建立团队成员间的信任来说,这是非常重要的,也是分布式团队中最先要面对的问题。通过视频会议设备进行回顾会议、展示会、站立会议和其他常规会议也是可行的。还有一种不错的技术,就是让每个开发团队使用屏幕录像软件录制一下他们在当天所开发的功能

3.7.2 集中式持续集成

  • 一些功能更强大的持续集成服务器提供像“集中管理构建网格”和“高级授权机制”这种功能,用于把持续集成作为一个集中式服务,为大型分布式团队提供服务
  • 虚拟化技术可以与集中式持续集成服务很好地结合,只需要单击一下按钮就能利用已保存好的基线镜像重建一个新的虚拟机

3.7.3 技术问题

  • 加大投入在各开发中心之间建立起足够高带宽的通信机制是非常必要的。考虑将集中式的版本控制库迁到某种分布式版本控制系统(比如Git或Mercurial)也是不错的选择
  • 一定要为每个地点的团队都提供所有系统的系统级访问权限,确保任何每个开发站的团队不但可以访问,而且可以修正那些与其换班相关的问题

3.7.4 替代方法

  • 在分布于不同地理位置的团队能够有效合作的重要因素中,持续集成算是仅有的两三种最重要因素之一。持续集成中的“持续”是很重要的

3.8 分布式版本控制系统

  • DVCS(Distributed Version Control System, 分布式版本控制系统)的兴起是团队合作方式的革命性改进。很多开源项目曾经使用电子邮件或论坛发帖的方式来提交补丁,而像Git和Mercurial这种工具让开发人员之间、团队之间以及分支与合并工作流时的打补丁变得极其简单
  • 与集中式系统相比,DVCS引入了一个中间层:在本地工作区的修改必须先提交到本地库,然后才能推送到其他仓库,而更新本地工作区时,必须先从其他仓库中将代码更新到本地库

3.9 小结

  • 如果本书所介绍的开发实践里,你只想选择其中一种的话,我们建议你选择持续集成。我们一次又一次地看到该实践提高了软件开发团队的生产率
  • 持续集成的实施还会迫使你遵循另外两个重要的实践:良好的配置管理和创建并维护一个自动化构建和测试流程
  • 一个好的持续集成系统是基石,在此之上你可以构建更多的基础设施
    • 一个巨大的可视化指示器,用于显示构建系统所收集到的信息,以提供高质量的反馈
    • 结果报告系统,以及针对自己测试团队的安装包
    • 为项目经理提供关于应用程序质量的数据的提供程序
    • 使用部署流水线,可以将其延展到生产环境,为测试人员和运维团队提供一键式部署系统

书籍

  • 《持续集成》
  • 《解析极限编程》
  • xUnit Test Patterns: Refactoring Test Code
  • Growing Object-Oriented Software, Guided by Tests

工具

  • CruiseControl家族(CruiseControl、CruiseControl.NET和CruiseControl.rb)
  • CruiseControl (http://cruisecontrol.sourceforge.net/)
  • Hudson (Jenkins前身,https://hudson.dev.java.net/)
  • LuntBuild (http://luntbuild.javaforge.com/)
  • JetBrains的TeamCity (http://www.jetbrains.com/teamcity/)
  • AntHill Pro (http://www.anthillpro.com/)
  • Bamboo (http://www.atlassian.com/software/bamboo/)
  • QuickBuild (http://www.pmease.com/)
  • 持续集成:Pulse、TeamCity和 ElectricCommander
  • 代码质量检查:Simian、JDepend、NDepend、CheckStyle、FindBugs

0 人点赞