作者简介:武小平(平晓),阿里巴巴测试专家,在CICD、自动化测试工具和质量管理方面有较多的经验,目前负责阿里巴巴研发协同平台阿里云RDC的测试。
转载来源:研发协同RDC微信公号(alirdc)
在欧洲中世纪的传说中,有一种叫“人狼”的妖怪,就是人面狼身。它们会讲人话,专在月圆之夜去袭击人类。而且传说中对“人狼”用一般的枪弹是不起作用的,普通子弹都伤不到也打不死它,只有一种用银子作成的特殊子弹才能把它杀死。Brooks在他最著名的随笔文章《No Silver Bullet》里引用了这个典故 ,说明在软件开发过程里是没有万能的终杀性武器的,只有各种方法综合运用,才是解决之道。
那么在软件研发过程中,哪怕没有银弹,如何用各种方法去解决这些“人狼”带来的威胁呢?阿里巴巴在多年的研发过程中,又是如何对付这头“人狼”?这一路走来,又有哪些方法和实践被沉淀?
阿里巴巴研发协同平台(AONE)是云上企业级一站式智能研发协同平台,目前为阿里巴巴集团,下属子公司以及生态合作伙伴提供从“需求->编码->测试->发布->反馈”端到端的持续交付服务,并解决研发过程中跨角色、跨组织、跨地区的协作问题,在此基础上通过数据驱动度量分析为组织效能提升提供决策依据,目前这一平台已经上云对外提供服务,称为阿里云研发协同RDC。(以下简称为RDC)
上图是整个RDC的业务框架,RDC采用微服务的架构,从规划到运营提供了业务全生命周期的服务,众多的服务造成了RDC的复杂度,另一方面作为一个面向用户的平台,RDC又涵盖了阿里三个层次的技术服务栈。
第一层,基础资源层,包括idc机房、网络设施、OS、Docker、数据库等。
第二层,平台服务层,包括中间件、调度层、应用服务器等。
第三层,RDC研发协同平台,包括 项目、代码、应用、测试、交付、运营的管理。
三个层次加起来涉及的核心应用达50+,随便一个风吹草动就会对系统的稳定性造成影响,这些影响最终体现到面向用户的RDC,因此好的质量保障才能为用户提供一个稳定,高可用性的研发协同平台。
总体采用 “事前预防”,“事中控制”,“事后总结改进”的思想,主要做的三件事情:
测试驱动持续交付
测试驱动持续交付,每一次的持续集成和发布都从单元测试,API测试,集成测试,UI测试进行自动化测试覆盖;具体包括:单元测试,集成测试,WebUI测试,移动端UI测试,压测,线上引流的API测试,线上冒烟测试。
(1) 机器监控报警,业务数据监控。
(2) 对线上运行日志的聚合分析,发现存在的错误。
(3) SLA数据质量提升,将业务数据可视化,指导改进方向。
(4) 面向业务的监控和故障演练,通过对线上7*24的监控提前发现问题保障系统的稳定运行。
研发质量提升
通过代码审核,集团规约扫描,规范代码和提升研发质量。
上图是测试驱动的的持续交付流程,也是RDC各个应用在发布时的持续交付流程,通过RDC的发布功能实现。
(1) 开发在变更(Change Request)中提交代码后,触发单元测试,检查代码中基本的逻辑问题,实践中单测的覆盖率和维护由开发同学自己保证,目前RDC核心应用的单测行覆盖率在50%左右。
(2) 部署测试环境,开发进行自测,主要对新的功能进行基本的验证。测试环境与生产环境完全隔离,具有独立的网络环境,单独的数据库和中间件等依赖环境。
(3) 自测完成后发布预发环境,由测试同学进行集成测试,包括回归测试和新功能的验证,以及对核心接口的小压测。预发环境与生产环境在同一个网络中,共享同一个数据库,数据隔离采用逻辑隔离,但是它具有一个独立的依赖环境,如独立的上下游和中间件。
(4) 接着进入Beta测试环节,通过截取一部分生产环境的流量到Beta环境回放来对核心接口做进一步的验证。Beta环境与线上环境完全相同,不同的是Beta环境不对外提供服务。
(5) Beta测试完成后,正式发布生产环境并对生产环境执行冒烟测试。
以上是一个变更的生命周期,也是一次完整的持续交付流程。得益于RDC发布系统和RDC实验室的集成,我们可以在持续交付的流程中实现测试的卡点,测试不通过,无法进入下一个阶段的发布。
以下内容重点讨论测试方法和实现方案,以及如何通过RDC完成持续交付,不涉及代码实现,部分服务也会在未来上云提供给外部用户试用。
传统的自动化测试更关注的产品UI层的自动化测试,而分层的自动化测试倡导产品的不同阶段(层次)都需要自动化测试。在《google 测试之道》一书,对于google产品,70%的投入为单元测试,20%为接口、集成测试,10% 为UI层的自动化测试。如下图的金字塔形,越往上的投入越低,按照代码统计的测试覆盖率也越低。
这些工具普遍存在的一个问题是不够灵活,工具本身比较重,调试麻烦;另一些比较轻量的工具又功能单一,针对性强,无法作为一个测试框架使用,测试场景比较简单时非常好用,一旦场景变复杂,就带来了大量的维护工作。
其次也开发过一些看起来很高大上的框架,有前端有后端,目的是减少测试人员的coding工作,一键完成测试,看起来很高大上。 但实际的问题是测试人员的主要工作变成测试框架的维护,最后的结果是业务绑定太强,无法推广,而自己的用例覆盖率也没有上去。
最后选择了TestNG作为主测试框架,其他的功能全部交给RDC完成。选择TestNG的理由:
(1)是灵活,作为程序员可以用代码实现灵活的场景组织和功能,只要稍微二次开发一下。
(2)是跟很多框架一样有Before,after,listener的概念,基于单元测试却比junit强大,对场景的准备,还原,清理都能较好的处理。
(3)使用java,也许不是好的测试语言,但是团队的技术储备java是强项,拥有丰富的开源测试库,能够完成单测,API,集成测试,WebUI,移动UI的测试;另一个原因是RDC本身也采用Java开发。
(4)缺点是冗余,作为一个测试框架,要实现测试比其他的测试框架要多一些代码。但是作为一个需要长期维护的测试工程,这些冗余也还可以接受。
如图所示,整个测试框架分为四层,通过分层的方式,测试代码更容易理解,维护起来较为方便。
第一层是基础测试库:包括selenium,macaca,ssh库,httpclient,cli库等。
第二层是服务层,相当于对测试对象的一个业务封装,如http的测试,是对远程方法的一个实现,对于页面测试,是对页面元素或操作的一个封装。
第三层测试用例逻辑层,该层主要是将服务层封装好的各个业务对象,组织成测试逻辑,进行校验。
第四层测试场景,将测试用例组织成测试场景,实现各种级别cases的管理、冒烟,回归等测试场景。
**测试调度**:主要用于集成测试和UI测试,因为这两个是对一个系统整体进行测试,RDC采用微服务的架构,整个RDC应用被拆分成十几个微应用,如果一个应用发布后测试过程中,另一个关联应用也发布了,那么这次测试结果的意义就不大。因此在执行中加入了调度机制,对关联应用进行调度,比如A、B是两个互相关联的应用,A应用发布后正在进行测试,B应用发布,这时我们的调度程序会将测试中断,然后B应用发布完成后,重新开始测试,最后将结果返回给A、B两个应用,同时完成卡点。
**分布归并**: 在测试集合中我们将用例进行组合,保证每个测试集合都在15分钟内,一旦发现时间增加则将测试集进一步拆分,然后将这些测试集并行执行,最后将整个结果归并,既能压缩时间,又能实现报告的统一。 所有用例在docker中执行,动态的分配测试资源。
关于单元测试的投入产出不同的公司团队有不同的认识,实践中认为单元测试的主要意义在于最低成本的发现问题,尤其对于代码的改动,相比集成测试阶段的问题更容易定位,并且良好的单元测试习惯能够使开发在编写代码时更全局的考虑问题。单测中主要需要解决的问题是外部依赖的隔离。
使用中Mockito可以满足大部分的需求,但是它的实现机制是使用cglib来动态创建接口的类的实例,这种实现方式不能用于构造函数和静态函数,因为这两个需要使用类的字节码(比如使用javassist),所以结合PowerMock一起。
关于测试评价,目前也没有特别好的评价体系,所以最常用的还是代码覆盖率,在工程的pom.xml中引入cobertura,排除不需要计入的第三方class,RDC单测实验室中的代码覆盖率工具会自动统计覆盖率。目前RDC的行覆盖率在50%,据了解行业内做的好的一般在68%左右。
集成测试是整个持续交付中最重要的阶段,稳定可靠的集成测试能够减轻测试工程师的压力,其次能促进开发和测试之间的相互信任,形成良好的互动。集成测试一般采用API的方式,在预发阶段进行,选择这一阶段的原因是环境较为稳定,测试发现的问题能真正反映系统的问题。
集成测试规模
集成测试按照集成的规模,分为小集成测试和大集成测试,小集成测试一般是在某几个系统之间进行集成,大集成测试在整个系统中进行,这两个主要区别是测试覆盖和测试时间,我们使用最在靠近用户UI一层的API进行大集成测试,这一层更能体现真实的体验,能用最少的投入产生最大的价值。测试时间通过测试调度控制,每一个子系统发布时检测是否有测试进行,如果有则停止再重新触发。
接入RDC
在RDC应用中有【测试验证】,选择【添加验证点】,设置阶段为预发集成测试(也可选择其他阶段),同时在此处可以设置卡点的条件,如设置测试通过率99%,当你的测试通过率小于这个数字时在发布流程的预发测卡点中将会报失败,防止将测试验证不通过的代码发布到线上。
UI测试是一个很繁杂的事情,投入产出比不高,UI测试最大难点在稳定性和维护性方面,因此我们在UI功能测试当中只覆盖主流程和最基本的页面检查,主要对主流程进行覆盖,如增、删、改、查等这些出错会造成系统不可用的功能进行覆盖。
UI测试另一个比较麻烦的事情是兼容性,由于各类浏览器的快速迭代经常造成selenium连接浏览器、获取元素失败。因此我们对selenium-grid进行了二次开发,搭建浏览器云,根据指定的浏览器类型,版本、以及selenium的版本等在指定的浏览器上运行,满足兼容性测试的同时,保证UI测试的稳定运行。
如上面提到的UI功能测试主要针对主流程功能性的逻辑测试,无法对页面元素和展现进行校验,因此我们对UI进行地毯式的校验,采用的方法是 页面元素+图像比对。
基本步骤:
第一步:使用selenium对RDC的各个页面进行爬虫,深度为主域名以下三级Link,并遍历出这些页面的元素,主要对文字、Button、Link、Form元素进行收集,保存到数据库中。
第二步根据前面录制的Link,分别检查页面能否正确打开,并校验这些页面是否包含存在的元素。
第三步爬虫脚本完善,由于个别页面的变化和对页面排版的要求,对导致测试失败的脚本进行完善,去掉动态元素,由于动态元素不是很多在整个测试过程中影响不大;另一个是对展示要求较高的页面加入图像比对,提前对屏幕进行截取(分块截取,动态内容不做截取,这部分在前面的功能测试中进行覆盖),在回放中会对相应页面进行截取,使用工具sikuli将两个截取的图片进行比对。
曾经有个故障,原因是开发的修改导致接口变慢。因此我们将压测加入到常规的测试卡点中,在每次系统发布前都会进行一次压测,将RT值进行对比。
目前针对Android主要采用instrument的方式,对ios则采用自带的的sdk的方式,随之而来的问题是针对不同平台需要两套测试代码。开源的macaca和appium解决了这个问题,这里选用macaca来进行测试,相比较Appium优点是运行稳定,去除了appium老版本Android的支持,而且macaca有专门的团队进行支持。使用macaca能够像selenium一样对移动端的界面进行测试。
接入RDC
目前RDC尚未提供真机环境,因此我们使用Jenkins管理真机环境,然后通过调用Jenkins的Rest接口,触发测试在真机环境上运行,执行完成后解析Jenkins的报告为RDC格式的结果,即可在RDC的实验室中展示测试结果,进而实现对发布流程的卡点。
传统的API测试,一个接口一个接口的编写代码,准备测试数据,添加验证点,工作量大,覆盖不全,测试效果不明显。这里我们引流生产环境的流量来进行API测试,原理是AOP的方法拦截。主要有三个步骤,线上流量复制,回放,写操作的Mock。
回放:同样采用ASM在测试环境(Beta环境)修改被测接口的字节码,触发接口的调用,在被测接口中使用上面录制的参数,接口执行完成后将录制的结果和这次执行的结果进行对比,结果一致则测试通过。
Mock:对于写操作,由于我们采用的Beta环境和生产环境采用同样的数据库和外部系统,因此到写操作不能真实的写入数据,需要在写操作的方法中直接返回上面录制的写操作数据。
接入RDC
在上面的录制环境和生产环境中,安装agent开启http服务,跟RDC实验室进行通信并控制测试的执行,最后的返回结果被RDC解析,从而实现发布卡点的功能。目前该系统正在改造,改造后将对云上用户开放。
测试报告同样采用了分层的报告,分为:实时报告,单次执行报告,统计分析报告。
对于机器监控有很多开源的方案,在此不再详诉,一般来说主要采集机器的CPU,内存,TPS,JVM等指标,一旦某个指标跨过红线,及时做出报警。
对于业务数据比较复杂,根据RDC的业务有几十种不同的监控指标,这里我们主要采用的是日志打点的方式,RDC的各个子系统的都对输出日志进行了改造,生产环境的每台机器会有一个专门解析日志的agent,这些agent增量的实时监控业务日志,并将日志解析得到跟业务相关的数据,发送给监控系统,从而监控系统能得到某个点或者某个时间段内的业务情况,根据划定的业务指标红线给出报警。 该系统目前正在进行改造,之后将提供给云上用户使用。
由于现在系统都采用微服务的方式,系统拆分较细,每个微服务又都采用集群的方式运行,因此系统只要正常运行,后台发生了多少错误几乎没有人关心,而这些错误有可能是导致故障的潜在隐患。
我们采用了阿里云的sls日志收集功能,将RDC相关应用的日志接入到sls,每天晚上00:00按应用对RDC当天产生的日志进行聚合分析,目前通过一定的算法对错误信息进行聚合,发现问题和缺陷,自动提交bug,并发送当天的统计报告。 目前通过日志的聚合分析,RDC修复了大量稳定性方面的错误,错误数由之前每天的十万量级降低到现在的千级。
将用户体验量化,如何为用户提供极致体验,一直是我们努力的目标,然后由于RDC上下游系统较多,如何评价现有系统,采用将服务体验数字化,通过将每一层的服务数字化来找到问题的瓶颈。
数据的收集采用了两种方式,一种是直接从数据库中提取数据,另一种是通过打点的数据提取业务数据。将这两种数据经过计算统计报表的形式展现,从而清晰的知道目前系统的情况。
如图是某一周的业务数据
通过这些数据能够了解到整个系统的瓶颈,从全局了解系统和用户感知的差距,并指定目标和改进计划。 目前的问题是无法智能的拿到这些失败或错误的原因,比如出版本失败率0.14%,这0.14%是什么原因导致的需要花很大的精力排查,如何智能的找到问题,减少排查工作是下一步要做的工作。
研发质量的提升目前主要包括代码审核、代码质量检查好研发效能数据的回溯。
对于自动化测试,希望能用更有效的发现问题,尤其在UI测试上,考虑如何用最低的成本获得最大的回报,进一步完善爬虫式测试回放的工具,增加测试的覆盖率。
线上质量的监控,希望能够对故障进行预测,目前我们做到的还是对故障的处理,比如故障发生10s内发现,3分钟能响应处理完成,但是如果能够对故障进行预测将极大的降低系统风险,所以下一步工作是对海量错误日志,故障数据,用户反馈数据,监控数据进行数据挖掘分析,并结合实时监控建立自学习的预警系统。