求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
iOS系统下的持续集成实践
 
作者 元耀,火龙果软件    发布于 2013-11-19
 

iOS系统下的持续集成实践!我们遇见的问题: 客户端项目总是希望可以更快的拿出一个可用的版本,尽早的让内部的同学试用这个新的客户端,尽早的收集意见,尽早的修复问题,优化设计;同样开发的同学在代码修改的过程中也希望能够更快的收到反应。

我们遇见的问题:

客户端项目总是希望可以更快的拿出一个可用的版本,尽早的让内部的同学试用这个新的客户端,尽早的收集意见,尽早的修复问题,优化设计;同样开发的同学在代码修改的过程中也希望能够更快的收到反馈。这就要求客户端的测试能够更快捷的响应,尽快的回归新版本,及时的反馈问题,尽早的发出稳定的新版本来提供内测。因此客户端的测试就产生了这些需求:

1) 开发提交代码之后尽可能快的发现和反馈问题;

2) 更快的进行功能回归,保证开发的修改不会对已有功能产生影响;

3) 尽快的提供一个稳定版本用于内测,以方便更好的提升产品质量;

从上面的需求上来看,就出现了一个迭代,发布一个新版本内测带来了使用者的反馈和建议,于是开发修改了代码以提升质量,于是又发布了一个新版本提供内测,新版本又会带啦新的建议和反馈,如此迭代着前进,于是项目质量越来越好,用户体验也越来越好,很美好的一个事情

但是往往现实比较骨感,这样的一个迭代节奏一旦被打破了,那么带来的往往是一种灾难,不停的发布新版本,结果问题越来越多,内测用户越来越少,整个项目组的人反而越来越累;

为了避免这样的问题发生,不让一些坏的味道破坏项目的节奏,让整个项目更好的运转就需要满足上面的这些需求,同时尽量让这些需求做到更加的自动,减少人的参与,本地生活业务线就在这样的情况下开始了尝试,去试着形成自己的节奏,解决自己的问题。

在这里说一说本地生活业务线的持续集成,和大家一起沿着这条路走走:

最初的分析:

首先想到的第一点,自动化。那么如何来做iOS客户端的自动化测试呢?话说站在巨人的肩膀上,对于本地生活也是如此;于是开始对于自动化的框架进行选型,调研了不少的团队,最终选择使用athrun来做本地生活的自动化框架,原因也很简单:第一,就团队和个人来说,对于Java比较熟悉,虽然使用Javascript可以带来录制的便利,但是从长远来看使用athrun对于团队来说效率应该更高,学习成本也会比较低;第二,使用athrun很好的和JUnit集成起来,而JUnit的框架比较成熟,带来断言,日志等便利,可以丰富脚本运行时的日志,便于后期对于结果的分析;当然了,最主要的原因还是因为第一条,学习成本比较低,对于语言比较熟悉;

继续深入下去,发现athrun在运行的时候需要提供一个app文件,如此带来了一个新的问题:如何保证我测试的这个app包是最新的,怎么更快的获取这个包,怎么减少人工干预?一般测试过程中工程都是通过Xcode来进行build的,那么是否存在另外的方式来解决这个问题呢?经过学习之后发现Xcode提供了命令行工具,通过这个工具就可以使用命令行来build工程,自动打包;那么是不是可以通过一段shell命令完成这个工作呢?于是写了一个shell脚本来进行项目的build工作,在每次执行自动化脚本之前build一下最新的包,那么自动化脚本的执行的app就是最新的了;

好的,自动化的工作由此展开,然后新的问题又出现了,项目进行到后期开发提交的代码的环境设置往往是预发布环境,而自动化运行的环境是测试环境,更悲剧的是发布到内测的版本的环境是线上环境,如此三个环境的切换开始了,懒人总是希望不要将这些繁琐的事情让自己来完成,于是想尽办法来偷懒,结果就选择了一个最笨的方式,通过shell来做环境切换;对于不同的环境建立不同的版本,在版本build之前先将环境做修改以满足不同的需求;

要发布第一个内测版本的包了,还是蛮开心的,丑媳妇终于要见公婆了,却被告知啥要提交一个ipa包,对于我这样一个之前从没有使过苹果的低端用户简直就是一个基础扫盲,被发配回去重新研究生成ipa包,开始的时候使用Xcode来生成ipa包,每次上传之前先自己生成一下,然后再上传。但是这对于一个懒人是怎样的一种折磨啊,于是去想办法偷懒,发现jenkins中提供了一个Xcode Plugin的插件,这个插件不但可以进行项目的build还可以生成ipa包,好吧,前面的功夫白费了,弃暗投明呗!

这三个多月以来一直在解决着自己的问题,在这个过程中遇见了很多问题,也解决了很多的问题,通过解决这些问题对于最初的三个需求有了更深的思考,于是随手拿起笔涂鸦一下。

进一步思考:

目前本地生活的iOS客户端已经有2个在AppStore上发布,在这两个项目中对于上面的部分都进行了实践,梳理出了如下的流程:

这个过程中形成了一套适合自己的持续集成的过程,在开发提交代码之前对于代码进行静态扫描,将除去第三方库以外的问题修复,之后提交的代码将会被Xcode Plugin进行build,在build过程中将将会对于不同的需求构建不同的包,在其中用于自动化回归的包上执行自动化脚本,对于稳定版本(自动化测试通过)的正式ipa包上传至内测版本下载服务器;其主要的步骤包括以下部分:

1) 更新最新代码;

2) 静态分析;对于代码进行静态检查,Xcode使用的静态检查工具为Clang Static Analyze,该工具可以监测出代码中潜在的内存泄露、使用未赋值变量、变量申明后未使用等问题,如果发现了相应的问题,通知开发进行修复(目前这一步骤没有通过自动的方式来实现,还是通过和开发的规约来保证,再之后会将这部分自动化实现);

3) 版本构建;针对不同的需求构建不同的版本,这步是通过Xcode Plugin来实现,Xcode Plugin使用的仍然是xcodebuild命令行,在这一步骤中需要提供环境切换的脚本,而不同的项目可能开发实现环境切换的方式都不同,因此这里经常是让人很抓狂的地方,需要和开发进行进一步的沟通共同确定环境切换的规约。在构建之后需要对于这些修改进行还原操作,不要遗漏修改的代码还原;

Xcode Plugin可以构建ipa包,因此在这一步可以将正式环境的内测ipa包生成出来;

4) 自动化验证;自动化脚本验证之后的版本就是稳定版本,可以提供给外部进行内测,因此自动化脚本要求覆盖应用的主要功能点,保证这些功能点的功能正确性,注意自动化验证的版本的环境一般是测试环境;

5) 内测版本上传;自动化验证针对的是测试环境,在自动化测试通过之后将相应的正式版本上传至内测应用下载服务器,该步骤需要和服务端约定一个更新的规约,如果没有规约则需要手动上传,注意如果没有办法自动上传需要将稳定版本进行存储;

上面说了本地生活在这两个iOS客户端测试中遇见的问题和一些解决办法,整理了一个大体的思路,那么在后面的部分针对每一部分进行详细说明,聊聊现在本地的做法,也说说自己的思路;

说说实践:

关于代码更新:

代码更新没有什么好说的,需要进行代码更新的有两个地方,一个是在每次build之前使用更新代码,另一个是在每次进行自动化验证之前更新自动化测试的代码;

一般来说工程的svn库和自动化测试脚本的svn库是分开的,所以在这里采用了最简单的处理方式,将整个流程拆开,拆分为2个Job,一个是针对客户端工程的代码库,该Job负责代码的静态检查和build工作,另一个是针对测试脚本的代码库,该Job负责自动化验证工作;

关于静态检查:

Xcode中集成了Clang Static Analyze检查器,通过该检查器可以对工程进行静态分析,通过分析可以找出代码中存在的潜在问题,如潜在的内存泄露、变量未赋值就使用、变量定义之后没有使用或者除零等逻辑错误;

目前为了保证版本发布的质量,减少潜在的风险和开发进行了规约,在每次提交之前对于代码进行静态检查并对检查结果修复;

在Xcode中使用Analyze的方式如下:

Product->Analyze

在上图中的左边栏中的蓝色标示就是存在潜在问题的地方,开发和测试的同学都需要对这些地方加以注意,对于每个问题进行分析;目前本地的规约如下:

1) 如果该问题是由于第三方库带入的,或者第三方库的问题在不需要修复;

2) 如果该问题发生在非第三方库的代码中,并且确认是一个问题就需要修复;

3) 如果该问题发生在非第三方库的代码中,并且确认不是一个问题不需要修复;

关于代码静态检查最好是针对iphoneos(最好不要采用iOSSimulator)的Release版本进行静态扫描;在Xcode中可以通过Product->Edit Scheme来进行设置:

那是否只能通过Xcode来进行代码静态扫描呢?有没有更加懒的办法来自动的进行这个工作呢?答案是有的,Jenkins提供了一个插件叫做Clang Scan-Build,通过这个插件可以对代码进行静态检查,该插件使用的也是Clang,和Xcode是一样的,该插件最终将生成xml的报告,在Analyze之后可以发布报告;

在jenkins中设置如下:

在我们的工程中的一些问题可能是第三方引起的,本工程中没有办法进行处理,而这些问题可能会导致认为工程的build版本为不稳定版本,该插件提供一种简单的方式来处理这种情况,插件提供一个阀值,当超过这个阀值的时候才会认为工程是不稳定的版本,这个时候只需要统计出没有办法进行处理的问题数目,然后将阀值设为这些问题的个数;

关于自动构建:

Xcode提供了命令行工具来通过命令行进行工程的构建,在Xcode4.5中命令行工具是默认不安装的,因此需要首先安装命令行工具,安装方式如下:

安装完成之后就可以使用xcodebuild命令来进行工程的构建了;

开始的时候工程的构建是通过shell脚本来进行的,后来发现在jenkins中提供了一个Xcode Plugin的插件,通过该插件可以可视化的配置build的参数,这个插件最终使用的仍然是xcodebuild的命令行;Xcode构建的配置如下:

这里的配置项的用法和说明在后面的提示中都有包括,那么说说项目中的一些需求,基本上现在的项目测试自动化脚本都是在测试环境运行,然后发布的内测包都是线上环境的,而又不想把这样的工作通过jenkins中的两个Job来实现,因此这里的处理方式是设置了两个Xcode Step,第一个构建测试环境下用于进行自动化验证的版本(上图),第二个用于构建内测版本的线上环境(下图);

两者之间提供脚本进行环境的切换,目前的问题在于由于各个项目的开发不同,因此用来做环境切换的方式也会不同,这样就导致了需要为每一个项目提供一个如下的环境切换脚本:

用于在构建测试版本之前把环境切换至测试环境;

在构建完测试版本之后要准备进行内测版本的构建工作,这个时候需要再将环境切换至线上环境;

这种由于项目中提供的环境切换方式不统一导致的问题需要在后面和开发形成统一的共识,以减少切换脚本的多样性,形成同一个脚本来进行修改,减少维护问题;

应用安装到iPhone上需要的是一个ipa包,因此需要将构建出来的正式版本打包成ipa包,然后提供给内测下载服务器,而Xcode Plugin提供了打ipa包的功能,Xcode Plugin制作ipa的方式是通过xrun命令行来进行的,打包ipa的配置如下:

在制作ipa包的过程中需要提供mobileprovision文件和签名证书(注意提供给内测使用的ipa需要的是企业级证书,因此注意检查工程的配置或者提供shell脚本修改project的配置);

关于单元测试:

在上面的部分其实少了一部分就是关于单元测试的内容,iOS提供了一个单元测试的框架SenTestingKit,通过这个框架可以对iOS进行单元测试,而Xcode Plugin允许在每次构建的时候执行单元测试,因此就可以通过Build来及时的把问题反馈给开发,也更便于开发定位问题原因;

那为何在本地的整个持续集成中没有这部分的内容呢?关于这个问题的考虑是这样的,本地这边客户端对于业务的封装是通过提供一系列的Service实现,这些Service构成了客户端业务的SDK,负责实现客户端主要的业务逻辑,但是目前这些Service比较“薄”,基本都是对与后端的http请求,然后将对应的结果包装成为对象返回调用方,因此这部分的测试可以通过对于后端提供的http接口的测试来覆盖,因此在这里缺少了对于单元测试的描述。

关于自动化验证:

在进行这些所有工作之前其实第一个想到的是自动化,希望可以通过自动化来对主要功能进行验证,减少成本;于是就对目前的自动化框架进行了调研,参考了其他团队的方式,最终选择了使用athrun来作为本地的自动化测试框架;

Athrun对于iOS自动化提供了底层的支持,而业务线需要根据具体的情况去搭建上层的结构,就好比搭积木,底层的积木块提供了出来,搭成房子还是搭成汽车就是上层的事情了;

分析了一下iOS客户端的特点,一般来说客户端的界面不是很多,一个基础的业务操作基本都在一个界面中就可以完成,很少会出现跨越多个界面进行操作的基础业务操作,其次就是客户端每个界面上需要输入的数据都不会很多,因此也不太需要提供专门的数据层来对测试数据进行支持;针对以上的特点对于业务的自动化测试脚本进行了分层,由上至下分为了四层:

1) 第一层是测试用例集层,该层负责整合测试用例集,物理的对于测试用例进行划分,按照一定的逻辑将测试用例整合起来;

2) 第二层是测试用例层,该层提供了实际的测试用例脚本,测试用例由业务操作组合起来,形成各自的业务场景;

3) 第三层是业务流层,该层提供了对于客户端应用的业务操作的封装,为测试用例层提供可复用的操作;

4) 第四层是元素层,该层提供了操作界面元素的接口,这部分完全由athrun来提供;

代码的组织结构如下:

测试用例由业务流的操作组合而成,同时添加必要的验证,测试用例的结构如下所示:

而这些业务流的初始化与清理工作完全由每个应用的测试用例基类来进行处理,测试用例基类的写法如下:

随着自动化测试的开展和新的项目开展,新的问题也一个个的出现了,这些问题包括:

1) 开始的时候没有使用jenkins,同时由于机器的IP地址没有办法固定下来,因此没有办法得到需要的测试报告,于是提供了一套机制来生成测试报告,测试报告的生成结果如下:

后来使用jenkins来进行持续集成,该测试报告生成方式被抛弃;

2) 电影票客户端数据每天都需要更新,如此数据准备的操作需要放到自动化中来解决,于是提供数据准备和数据清理工作,提供@DataPrepare和@DataClear来进行数据准备和数据清理的配置;

3) 测试环境有时候不太稳定,一些测试用例在第二次运行的时候就可以通过,问了解决这个问题提供了@Rerun标签来对失败的测试用例进行再次执行进行配置,允许设置重复执行的次数;

4) 后来环境就更恶劣了,已经不是靠重复执行就可以解决的,同时也希望当测试环境有问题的时候就不要运行测试用例,为了解决这个问题提供了@CheckEnv标签来检查环境;

这些部分的特点都是与业务基本上没有相关性,因此在这里的做法是将这部分独立提成一个工程,该工程为各个业务的自动化脚本提供支持,同时在这个工程中处理了运行自动化脚本时的内存泄露和异常退出时再次运行无法启动app的问题;

目前客户端和服务端的交互一般做法是通过http请求进行的,服务端处理之后将结果用json串进行返回,因此针对这样的协议在框架工程中添加了对于http的封装,通过这部分可以实现在对于服务端接口的测试,因此在同一个业务的自动化工程中可以进行UI自动化测试,同时也可以进行服务端的接口测试;

在这里的另一个问题是任何协调自动化验证的版本和build出来的版本,目前是做了这样一个约定,统一一个地方存储构建之后的版本,build成功后将构建出来的新版本放置在这个约定位置,然后自动化脚本配置执行的app位置就是这个约定位置,从而保证自动化运行的app都是最新的成功版本,并且不需要通过人工的方式来进行同步;因此在build的步骤中加了一步在每次成功构建之后将构建的结果放置在这个指定位置:

然后在自动化脚本的配置中按照如下的方式指定运行的app位置:

自动化测试运行需要时间,如果每次在代码提交之后都运行自动化测试验证的话可能不一定合适,同时新版本的更新也不需要每天都更新几次新版本,目前本地的新版本更新基本上一到两天一次比较适合,因此设置自动化验证Job的执行策略是每天定时执行一次;

关于更新ipa包:

到这里故事就走到要结束的部分了,构建了稳定的新版本之后的下一个任务就是把新版本发布内测,这个部分定义在自动化验证的Job中,在自动化验证的步骤之后增加一个新的步骤用于上传ipa包,这一步需要内测版本下载服务器提供一个接口或者一种策略来支持ipa的上传和更新,目前本地的内测版本下载服务器的这个功能还在开发中,因此ipa包的更新还是靠手工上传;

下一步展望:

目前的这个方式不是最好的方式,那么如何把iOS的持续集成做的更好,下一步又该是怎么样的呢?在这里说说自己的一点看法:

1) 目前这一套的东西游离在kelude的框架之外,而看看Xcode Plugin这些的最终实现仍然是使用的Xcode的命令行工具,那么理论上这个就应该可以统一起来整合在我们自己的框架之下;

2) 目前整个流程中缺少了单元测试的这部分内容,由于本地的业务目前单元测试实施的意义不大因此忽略了这部分内容,在之后客户端形成自己业务SDK之后需要将这一部分补充上来;

3) 目前代码的静态检查主要还是依赖于和开发之间的约定,依赖于Xcode来进行,虽然jenkins提供了Clang Scan-Build Plugin插件进行集成,但是目前还没有将这部分内容很好的集成进来,在之后需要对这部分进行补充,同时过滤第三方的问题,而把关注点集中在自己的项目上;

4) 目前环境切换脚本,构建和自动化应该可以更好的协调在一起,而不是采用目前这种临时的笨办法;

5) 新的内测版本的上传更新可以做到更加的自动化,而不需要在手动的将稳定版本上传至服务器上;

写了这么多,更希望的是抛砖引玉,希望收到大家更多的建议和反馈,更希望可以从大家那里学到新的东西,抛弃目前的一些浅显的做法!

相关文章

为什么要做持续部署?
剖析“持续交付”:五个核心实践
集成与构建指南
持续集成工具的选择-装载
相关文档

持续集成介绍
使用Hudson持续集成
持续集成之-依赖管理
IPD集成产品开发管理
相关课程

配置管理、日构建与持续集成
软件架构设计方法、案例与实践
单元测试、重构及持续集成
基于Android的单元、性能测试
 
分享到
 
 


集成与构建指南
项目管理:Maven让事情变得简单
持续集成工具hudson
持续集成
Maven权威指南
程序集(UML中的包)之间循环
更多...   


产品发布管理
配置管理方法、实践、工具
多层次集成配置管理
使用CC与CQ进行项目实践
CVS与配置管理
Subversion管理员


海航股份 重构及持续集成
电研华源 设计原理、建模与重构
软件配置管理日构建及持续集成
单元测试、重构及持续集成
中国软件研发中心 单元测试与重构
单元测试、重构和持续集成实践
罗克韦尔 C++单元测试+重构+Gtest
更多...