持续集成(CI)历程记

未来已来2018-09-10 14:01
作者:吕泽廷

持续集成大师Martin Fowler 在大作《持续集成》中对持续集成做了明确定义,认为持续集成是一种软件开发实践,但实践不是工程,是不可全盘复制的,于是我们必须围绕持续集成的思想来打造符合我们项目实际情况的实践。从思想上来说,我们希望开发尽早的提交代码进行系统的集成,并通过一系列的自动化测试来发现其中的缺陷,进而尽早尽快的解决这些缺陷。
    那么如何去实现这一整套CI 流程呢?本文主要从记实的角度讲述Lofter 项目是如何走持续集成的道路。下图是整个CI 流程的简要图示:
说明:CI 持续集成;UIT 用户界面自动化测试;IT 接口测;UT 单元测试;SA 静态代码检查

一键部署
    在项目的石器时代,大家都是用SSH 远程编译服务器和部署服务器进行测试环境的编译部署。同时脚本是分离的(即包含两个脚本:编译脚本和部署脚本)。在那个时代都是用这种部署方式,虽然习惯成自然,但测试人员最终还是发现手工操作、漫长的等待、访问服务器、环境的切换这一系列日常工作中必不可少的环节,其实是相当浪费时间且枯燥无味的。
    这时Jenkins 出现了,通过在Jenkins 上配置Job,可以将不同服务器的协作集中在一个平台上完成。经过风风火火的尝试,渐渐地我们将所有的环境部署从死板的命令行界面转移到了高贵优雅的UI 界面上。同时,测试人员也切实体会到了一键部署的乐趣,从效率上、反馈上都快速的摆脱了纯手工的繁琐、低效、易误操作。

UI 自动化的验证
    从手工部署跨度到平台化的一键部署,俨然已经进入了铜器时代。渐渐地QA 同学发现,高效的一键部署带来的“副作用”是频繁地回归测试,自己慢慢地显现出力不从心了。此时天空一声巨响,UI 自动化闪亮登场。在当时的大环境下,业界同行都在如火如荼的尝试和实践UI 自动化,频现让人兴奋不已的成果。
    经过一轮努力和尝试,我们慢慢建立起了UI 自动化体系。部署后可以将大部分重复的手工回归工作交付机器去完成。我们如同进入了铁器时代,锻造出QA 第一把严格意义上的武器(Dagger)用于实践UI 自动化。QA 也开始慢慢享受冷兵器时代狩猎(找Bug)的乐趣。

CI 的雏形
    同样的,Jenkins 平台可以提供job 调度和计划执行。慢慢的我们意识到为什么不把编译部署和UI 自动化串联起来,自发的去执行呢,自动的去检查当前代码是否由于某个开发的提交引入了一些明显的Bug,导致代码的不稳定。
    经过Jenkins 的配置调整,我们CI 的小火车开始慢慢的跑起来了。每次编译部署后,都会执行丰富的UI 自动化测试,然后反馈当前代码质量。

自动化测试的演进,UT 和IT 的引入
    进入蒸汽时代后,CI 的小火车在那里欢快的奔驰着。当然每个时代发展到一定程度后,都会衍生出各种各样的矛盾和冲突,我们也慢慢地遇到了瓶颈。随着产品的茁壮成长,UI自动化体系也越来越庞大,渐渐地表现出“过度肥胖”,“吃得多,跑得慢,局限性大,禁不起折腾”。UI 的维护成本高、运行速度慢、分支异常覆盖难度大,稳定性差等问题使我们焦头烂额。显然我们需要突破,需要大马力的跑车,需要驰骋起来。
    这时候我们发现了UT(单元测试)和IT(Http 接口测试)这两个跑车界的小钢炮。IT我们定义为:针对系统暴露给前端(包括浏览器,客户端,第三方)可以调用的接口,通过过发送Http 请求方式进行的测试过程。接口级别的用例执行是毫秒级别的,同时可以轻松覆盖系统大部分的业务逻辑,相对UI 可以更底层的保证产品质量,且稳定性高,维护成本低。发送Http 请求方式进行的测试过程。接口级别的用例执行是毫秒级别的,同时可以轻松覆盖系统大部分的业务逻辑,相对UI 可以更底层的保证产品质量,且稳定性高,维护成本低。
    Unit Testing 是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。对于面向对象编程,最小单元就是方法。我们知道UT运行速度都是毫秒级别的,快如闪电,同样UT 主要的测试对象为运算逻辑和业务逻辑,稳定性和维护成本也相对较低,同时我们的系统是基于Spring 的,有很多易用的拓展可以提高效率(其中最为突出的就是事务的Rollback)。
    面对这块新大陆,我们迅速找最忠实的战友——开发,帮助我们解决框架上的问题,并且和我们一起来完成UT 的编写。作为一个正规项目,程序是有设计分层的。面对这一
系列的分层又该如何去覆盖我们的UT 呢?在和开发协商讨论后,将UT 作用层次划分为:Dao 层(数据库操作层),Service 层(基础业务服务层),Control 层(包含控制逻辑的完整业务操作层)。通过开发和QA 共同协作来完成Service 层和Control 层的覆盖,同时将UT 也加入到我们的CI 中(我们的开发同学相当的给力,会在新功能完成后编写自测UT,这也是相当好的习惯,彰显出高级工程师所独有的气质)。
    当然仅仅只有UT 还是不足以全面的反应整个产品,因为我们不知道一个用户请求是否能够成功访问到我们的功能,那些系统级配置项是否正确,这时候我们适当通过IT 来弥补UT 的不足,做到有的放矢,从容不迫。这样我们就欣欣然地走向了电气时代。

自动化的万里长城-SA(静态代码检查)
    有了UI、UT、IT 已然大大缩短了我们的人工成本,提高了效率,但是这三种自动化往往需要我们投入人力去编写,才能保证相应代码质量。那我们是不是可以找一种更加通用的方式去检测代码的质量问题呢,犹如万里长城一般?不得不说唯有SA 才能担此大任。怎样算是一款高质量的软件产品,这着重体现在代码的软指标上:可测、可读、可维护、可拓展。代码检查的目的不单单是为了提高软指标,而是已深入到程序逻辑、内存检测、复杂度、优化建议等高级层面,进而影响程序的功能和性能。有了SA,项目自然而然地向电气时代的巅峰进发。


CI 的狼烟
    有强大的UI、UT、IT、SA 的层层防护,我们开始思考如何让我们的测试结果高效快速的反馈给相关人士来提高效益。业界备受推崇的警报灯方式在我们相对比较开放的办公环境,确实不是非常适用。敲锣打鼓式的反馈过于嘈杂,那么就采用邮件式的“狼烟”传递信息。失败的报告,迅速反馈给相应开发。闪电邮的气窗会如同狼烟般出现在相关人员的屏幕上,从而在不影响他人的情况下,让开发人员及时收到反馈并处理问题。不知不觉我们又进入一个崭新的信息时代。

CI 的自适应
    我们的CI 在信息时代下,已经如火如荼的运转着,婉约而不失高雅。但“流水不腐,户枢不蠹”,我们的产品在向前迈进,那么我们的测试范围也需要跟随产品的变动而做出适当的调整。所以所有类型的测试回归集合都需要根据每次版本的迭代进行优化调整,最大程度适应产品的变更,迎接着AI(人工智能)时代的到来。

CI 的进化
    当CI 走到AI 时代的时候,CI 已经可以迅速响应上线,此时我们的自我要求越来越严格,我们开始琢磨既然可以上线,为什么不马上上线,让用户尽早体验我们产品的新功能。同时我们又不想影响用户正常使用,不停服的发布就成了我们的不二选择。抱着技术问题永远只是时间问题的心态,开发同学依赖Nginx 的安全检查,通过分批更新tomcat,同时优化tomcat 的初始化和载入顺序,完美的实现了不停服发布。有了不停服发布后,结合现有的CI,我们不知不觉已经走向了持续交付(CD)。
    经过整个磕磕绊绊的过程后,我们的CI 体系总算初具规模,步入了CD 的正轨。CI 会监控每半小时内的开发提交,根据提交内容进行构建部署,实行UT、IT、UI 及SA。当遇 到失败用例执行,将相关结果报告report 给相关人士,进行定位修改,保证当前开发代码稳定可控,并最大程度缩小QA 人员手工回归的工作量。同样在每个迭代后,根据产品变更,调整各类型的回归集合,保证测试覆盖率是适应产品的。通过这一套体系的运作,开发同学可以第一时间发现自己提交代码所引入的问题并修复,且不影响其他同事的工作。QA 人员可以把精力集中在新功能的测试,且将回归成本降到最低。同时可以支持不停服发布,只需要测试通过就可以马上发布让用户尽早地体验产品新功能。



本文来自网易实践者社区,经作者吕泽廷授权发布