http://www.infoq.com/cn/news/2011/01/ci-check-in-dancehtml
【编者按】众所周知,敏捷软件开发方法中有多种最佳实践,既有管理方面的,也有技术方面的。在尝试敏捷之初,并非每一个团队都能使用所有最佳实践,也不是每一个实践都能在短期内见效。但其中有一种最佳实践倒是团队的必选,那就是持续集成,但这并不表示持续集成很是容易。数据库
尽管Thoughtworks的首席科学家Martion folwer 为“持续集成 ”下了定义,但因为自身背景与经历的不一样,每一个人对其都有不一样的理解。从狭义上讲,持续集成能够认为是一种基于某种或者某些变化对软件系统进行的常常性的构建活动(注:这里的构建活动不只指编译打包工做,还包含各种自动化测试、部署及发布活动)。然而,它忽视了一点,即:任何实践中都应该包含“与人的交互”这一因素。所以,从广意上讲,持续集成应该是软件开发团队在上述活动的约束下所采用的整个开发流程及活动。它强调开发团队与持续集成系统之间的互动性。咱们既见过持续集成作得很是成功的团队,也见过效果不佳的持续集成,甚至失败的案例。服务器
那么,到底如何从持续集成中获得最大的收益呢?这要从持续集成所涉及的诸多方面进行分析,并根据团队具体状况(好比团队规模、人员组成以及是否为分布式团队 等)及所开发软件自身的特色(是企业应用软件,仍是中间件?是嵌入式软件,仍是互联网产品等)制定实践策略与实现步骤。本专栏将与你们共同探讨与持续集成、持续部署及持续交付相关的方法、工具与经验。做者本人在Thoughtworks公司曾参与的一款持续集成与发布管理产品Go的交付和对外咨询服务为专栏提供了颇有素材,同时感谢肖鹏 、 李彦辉 、胡凯 、李剑等对栏目内容的支持和帮助。网络
在软件开发中,持续集成实践可以解决的问题是尽早的集成和尽早的反馈。所以,尽管目前流行的全部版本控制工具都提供了分支/合并功能,但在少于20人的团队中实现持续集成的话,推荐使用Single Branch开发策略。这样会减小多分支开如在合并时的开销。另外,因为理想状况下,每一个分支都须要有专属的持续集成环境(包括持续集成服务器、构建环境和测试环境等),因此Single Branch也减小了对持续集成环境的需求量(当编译时间较长或测试用例较多时,这个因素的影响尤为重要)。分布式
当团队完成最初搭建持续集成服务器,编写好一键式编译和测试脚本工做后,就须要考虑如何利用持续集成环境高效地进行团队协做开发了。必定有人会问:ide
“多人同时在一个分支上开发的话,每一个人提交时都要合并代码,不是更浪费时间吗?”工具
这个问题也正是持续集成指望解决的问题。每当开发人员提交代码时,就是其与其余开发人员工做成果的一次集成。若是每一个人都可以频繁提交代码,那么代码集成的频率就会提升,在持续集成的有力支持下,代码中潜在的问题就会更早地暴露出来(好比代码编译连接问题,自动化测试失败反映出来的代码功能问题,或需求理解不一致等问题),以便团队尽早解决之。单元测试
固然,持续集成所鼓励的频繁提交并非指那种仅将版本控制库当成备份工具,无约束的“随意”提交,还须要团队开发流程约束的。下面咱们来一同探讨“持续集成环境中的团队开发流程是什么样的”。测试
让咱们先设想一个软件开发场景。ui
故事的主人公叫Joe,他打算写一个游戏,因此用Subversion创建了一个版本控制库用于保存代码,而后就动手写代码了。Joe的开发流程是这样的。
如图1所示。
“每次在本地手工运行自动化测试太麻烦了,”Joe想到,“这种重复的工做为何不让机器来作呢”。
因而,Joe上网查了一下,发现持续集成工具是作这个事情的,就找来一台旧机器,用CruiseControl搭建了一个持续集成服务器。他的开发流程也变为:
两周后,游戏初见原型,Joe向他的几个朋友介绍了他的游戏建立,他们都很是喜欢,所以也加入了游戏开发。麻烦很快就出现了。持续集成服务器中构建结果常常失败,因此每次检出代码后都要作问题清理工做。因而,Job与朋友们坐下来讨论如何解决这个问题。
Alice说:“咱们每一个人都拉一个独立分支,当每一个人的功能开发完成之后,再合并到一块儿不就好了吗?”
Joe不一样意这样的作法。“游戏的需求还不明晰,要常常合在一块儿看一下效果。因此仍是在同一个分支上开发吧。下面,咱们讨论一下如何让这种失败少一些吧。”
因而,他们花了点儿时间,发现有两个主要缘由致使失败。
Joe提出,开发流程应该变成如图3所示:
但是,Alice提出反对意见。她认为:“既然本地已经运行了测试,为何还要在持续集成服务器上再次运行呢?”
Joe解释到:“主要是由于咱们每一个人的本地环境都不彻底相同,极可能出现‘它在个人机器没有问题呀’的这个现象,因此仍是要在独立的持续集成服务器上再运行一次。”
所以,你们就这么决定了。
四周后的一天,Joe花了很长时间完成了某个新功能后,打算提交了。因而他把分支当前的代码与其本地代码进行了一次合并。而后运行了本地测试,但测试失败了。他用了很长时间来定位该问题是在他本身修改的功能里,仍是在被合入的代码中。这让他对提交流程进行了反思。
“要是在合入他人代码以前,可以先运行一次本地测试,验证一下个人代码没问题就行了,反正本地测试所花的时间也不长。”
因而,他把这个想法告诉了其余人,最后大部分人都赞成这么作。因而,其提交流程就变成了这样:?
这个过程就被称为“Check-in Dance”。
Alice还说道:“咱们在从主分支上检出代码时,必定是那个经过持续集成验证的最新版本。这样能够避免检出的代码就是有问题的,而影响本身本地的代码。”整个过程如图4所示。
过了几天,有人把你们叫到了一块儿,此次是Alice。她说:
“我今天遇到一个问题。我提交代码以后,正等着持续集成服务器返回结果呢,Bob就提交代码了。幸亏我提交的代码经过了测试,不然的话,我就要在Bob的代码之上修复啦。因此,我建议咱们须要设立一个提交令牌,只有拿到这个提交令牌的人才能提交。也就是说,当一我的作完本地测试以后,去拿这个令牌。拿到以后,再进行代码合并、本地测试和提交。提交之后当持续集成服务器返回成功经过的结果时,才能交还令牌。这样就不会出现我和Bob这种状况了。”
可Bob并不一样意这样的作法,“此次没有出什么问题,为何还要这么作呢?”
此时,Joe把话接了过来,说道:“Alice的这个建议很好,我已经赶上过一次这样的事情了,那次测试失败之后,我花了很长时间才发现问题并不在个人提交中,而是在Mary的提交中。我把它修复后,又作了一次提交。”因为大多数人都赞成这么作,所以团队决定试一试。由于目前测试运行时间很短,因此提交和集成工做没有遇到什么瓶颈。提交流程如图5所示。
彷佛事情到这里就结束了。然而,这个游戏被某投资公司看中,决定作更大的投入,招更多的开发人员,让它成为一个开放游戏平台。那么,接下来Joe与他的朋友们还会遇到哪些问题呢?
http://www.infoq.com/cn/news/2011/02/ci-test-triangle
随着软件产品新特性的不断增长,软件自动化测试用例的数量也会成倍增加。对于一些历史“悠久”的遗留系统来讲,甚至会积累数以万计的自动化测试用例。若是对这样的系统进行持续集成,还要求每一个开发人员都要进行本地验证的话,困难的确不小。让咱们仍是看看Joe的团队是如何解决相似问题的吧。
在《戏说Checkin Dance》一文 中,我们说到:Joe?的团队实施了带有令牌的持续集成提交流程纪律。因为每一个人都作本地构建进行验证后再提交,因此持续集成平台上的构建结果比较稳定,天天持续集成服务器上的构建最多只有 一两次失败(常见的缘由是忘记提交某个文件而致使失败,和因本地环境配置与平台环境配置不一致而致使失败),但通常都能在30分钟内修复。随着项目的进 行,新功能不断地增长,自动化测试用例也越积越多。因为不作任何修改,本地构建脚本就会运行全部自动化测试用例,因此本地构建的运行时间也愈来愈长。团队里有人开始抱怨,“每次提交代码前,运行本地构建都超过15分钟,这样太浪费时间。咱们能否把那些不过重要的测试拿出去,再也不运行了?”
1、自动化测试黄金三角形
做为团队的技术负责人,Joe把你们叫到一块儿,就这个问题进行了专门的讨论。
“咱们不能放弃运行这些测试。”Alice说道,“在我前一个项目中,咱们就是这么作的,结果,这些花精力写的测试都做废了。”
“那是为何呢?”?Bob问道。
Alice回答道:“由于并不常常运行这些测试,随着功能的修改,有些测试的逻辑就再也不是正确的了。而当再次运行发现这类问题时,一般的结果就是把这个测试删掉,由于修复这个测试的工做量太大了。”
“那持续运行全部测试的话,等待的时间太长了,也是一种浪费呀。?”Bob说道。
此时,做为团队技术负责人的Joe说话了。“让咱们先分析一下,到底有哪些什么缘由让咱们的测试在这么短的时间里就变成须要这么长时间了呢?”
“功能增长的多了,测试天然就多了呗。”
“功能增长了,自动化测试数据的准备工做也多了,须要的时间固然就长了。”
“如今咱们的测试中有不少地方须要测试在原地等待结果返回,因此等待时间也挺长的。”
“你们还有没有其它缘由?”Joe追问道。
你们沉默了一下子,Bob说道:“好象主要就这些缘由吧”。
“那好吧。功能多而致使测试多这是好事儿,说明咱们你们都很是重视咱们的自动化测试。对于‘测试准备时间变长’这个问题能够理解,由于咱们的产品愈来愈复杂了。对于‘结果返回的等待问题‘嘛,须要具体问题,具体分析。前几天,我看到一个‘测试黄金三角形’,讲的就是自动化测试中各种测试的应具备的比例关系,对我颇有启发。我在白板上画一下吧。”因而,Joe走到白板前,将这个测试黄金三角形画了下来,如图1所示。
而后,Joe将这个图形解释了一下。原来,这个三角形讲的就是单元测试、集成测试和验收测试的关系。首先,左边向上的箭头表示,越高层次的测试维护成本越高,运行时间越长。所以,对于单个测试来讲,单元测试运行最快,维护最容易,而集成测试次之,验收测试则最高。?每类测试的面积表明着该测试的数量。如今,业界有不少种工具支持单元测试,所以它的编写及维护成本相对其它两种测试来讲较低,应使用单元测试对代码作尽量多的测试覆盖。通常来讲,单元测试覆盖率达到70~80%是比较理想的状态。
接着,Joe问了你们一个问题:“咱们产品中的这些自动化测试属于哪一类测试?”
Alice说道:“那要看你怎么定义单元测试中的这个单元。”
“根据WikiPedia上的定义,一个单元是指应用程序中最小可测试的部分。既然咱们使用面向对象的开发语言C++,那么单元测试的粒度应该是类中的一个方法吧。并且,一般来讲,若是一个测试包括如下任何一个情形,它就不是一个单元测试:(1)须要链接数据库;(2)须要网络通讯;(3)须要与文件系统打交道;(4)不能和其它单元测试同时运行;(5)须要对环境进行一些配置(如编辑配置文件)才能运行它。”Joe回答道。
“要是这么说的话,咱们的测试中,一部分是模块集成测试,一部分是验收测试,只有一小部分算是单元测试。咱们的测试集合正好是一个倒三角。”Bob边说,边在白板上画了出来,如图2所示。
“既然高层次上的测试(集成测试和验收测试)维护量比较大,从此咱们应该加入更多的低层次测试(单元测试),对于关键功能进行集成测试和验收测试。若是对于测试用例具备等价性的话,咱们应该用低层次测试来实现。这样咱们就会达到自动化测试的黄金三角状态啦。”Joe边说边在白板上笔划着,如图3所示。
“我赞成你说法,可是仍旧没有解决咱们目前遇到的问题。如何解决咱们如今本地构建时间太长的问题呢?”Alice有点儿不耐烦地问道。
2、分阶段构建?
“这还不容易,Martin Folwer(敏捷宣言的创造者之一)已经给出了一个解决方案,那就是两阶段构建(Secondary Build)。也就是说,咱们能够把那些运行比较慢,时间比较长且基本上不会失败的自动化测试用例挑选出来,组成一个新的测试集,在第二阶段运行,能够叫作‘二级构建阶段’。剩余的测试集仍旧放在第一个阶段运行,咱们能够把第一个阶段叫作‘提交构建阶段’。”Joe回答道。
“那什么时间运行这两个阶段的构建呢?”Bob问道。
“提交阶段构建固然就是在咱们每一个人提交以后就运行啦。并且在咱们提交以前,做为本地验证集合,在咱们开发环境上也要运行一样的提交构建。通常来讲,本地构建和提交构建最好都在五分钟内完成,最长也不要超过十分钟,不然开发人员就不肯意花时间作频繁地代码提交啦。另外,一旦提交阶段构建成功之后,就立刻自动触 发第二阶段构建。而咱们开发人员在持续集成服务器上的提交阶段构建成功之后,就能够继续进行其它的工做啦。”Joe说道,“咱们原来的六步提交图就变成这 个样子了。”说着,Joe拿起白板笔就画了出来,如图4所示。
“不对,这里有问题!持续集成强调尽早反馈。若是把测试分红两个阶段了,那反馈周期不是加长了 吗?”Bob反驳道。
Joe 点点头,说道:“你说的没有错。可是,根据咱们现有的软硬件资源条件,咱们目前还没法经过增长资源的方式来缩短全部测试运行的时间。因此咱们必须在质量与速度以前作出平衡。这也是我为何要把那些不易出错的自动化测试集合放在第二阶段构建的缘由,这样能够下降但不能彻底解除第二阶段构建失败的风险。因此, 这也要求咱们你们当第二阶段构建失败时,也要找人尽快把它解决,而且把相关的测试再次放回提交测试阶段中运行,或者在提交测试阶段加入新的测试来补充。” ?
Alice此时插话,问道:“既然第二阶段构建不常失败,为何咱们不定时运行它,好比天天晚上运行一次呢?这样不是更节省资源吗?另外,若是第二阶段构建运行得慢,那它不是一直都落后吗?”
“由于每次提交阶段构建成功之后就触发第二阶段构建,这样不管如何都比天天晚上运行一次的更多的反馈。由于天天晚上运行一次的话,若是出了问题,咱们只能在次日早上才能发现。对于你的第二个问题,我画一张图来解释。”Joe找了一张大白纸,在上面开始画了起来。
一下子功夫,几个示意图就画好了。看到这几个示意图之后,你们恍然大悟。如图5所示。从图中咱们能够看到:
“那根据咱们持续集成纪律,谁的提交让构建失败,就由谁来修复。若是版本125的第二阶段构建失败了,就包括版本124和125两次提交的变动,由谁来修复呢??”Bob接着问道。
“这个好办,由这两个提交人一块儿负责修复。若是想确切找到谁的提交有问题,还能够手动触发版本124的第二次构建。假如构建成功,说明版本125有问题,假如构建失败,说明问题在版本124就引入了。”Alice抢着说道。
讨论到这里,团队成员都达成了共识,(1)开始增强单元测试的力度;(2)在反馈速度和反馈质量之间作出折衷,使用二级构建构建的方式。
整个产品的开发很是顺利,立刻就要进行版本发布了。团队还会遇到什么问题呢?他们是如何解决的呢?请听下回分解。?