案例

移动应用开发方面,Etsy的持续交付流水线探索

在很久以前,本站报导过Etsy如何实现Web端每日在生产环境上部署40次。现在移动应用大潮来了,它又是如何做的呢?本文发表于2014年2月,虽然有一些迟了,但相信对广大读者还是会有很多启发,也会带来更大的信心 移动app的好评在用户转化率和品牌树立方面都有非常积极的帮助。相反,差评可能会有非常严重的后果。正如很多人所说:“移动应用的死活取决于它在应用市场中评分” 上面这个图是Etsy iOS应用的一个真实反馈。做为Etsy的开发人员,看到它真的很沮丧,但它是事实:有一些bug会从我们的指缝溜走,却被用户发现。对于Web端开发,我们使用我们的持续交付常规武器(从一些传统软件公司看来,可能是核武器)作为安全网来快速解决那些溜达到生产环境中的bug。然而,移动应用的发布需要第三方的审核(应用商店),平均耗时5天以上。 即使审核通过,是否升级,什么时候升级都是用户自己说的算——他们可能一直停留在某个旧版本上。根据我们的分析数据,Etsy目前有5个iOS版本和10个Android版本被用户使用。 通过持续集成 (Continuous Integration ,通常简称CI),我们能够在项目的开发和测试阶段发现并修复大部分问题,不至于影响到用户的体验:本文揭示Etsy对安卓和iOS应用实现持续集成流水线的过程。 每次提交(git push)都要在主干上,并在集成服务器上构建。 这是持续集成的第一基本原则,也是问题一旦被引入,能够快速发现的第一步:编译失败。在IDE里构建我们的应用并不算持续集成。感谢上帝,iOS和Android都是命令行友好的:构建iOS应用只要一个简单的命令: xcodebuild -scheme "Etsy" archive 准备持续集成的环境 用于集成的机器应该与开发者的机器分开——它们要为构建和测试提供一个稳定、受控、可再生的环境。“确保所有用于集成的机器都是幂等的”这一点至关重要。为了确保统一性和可扩展性,使用一个环境准备(provisioning)的框架来管理所有的依赖是一个不错的方法。 在Etsy,我们很高兴用Chef来管理我们的基础设施 – 我们用它来准备我们的Mac Mini机器集合。在安装包管理的homebrew以及方便管理ruby环境的rbenv帮助下,我们的系统运维师Jon Cowie 施了一点点hdiutil小魔法(来管理disk images),我们的cookbooks也就准备好了。对于我们构建和运行测试所需安装的Xcode,Git,以及所有Android包来说,其中95%的工作,我们已经通过编程方式实现安装了,还有一些步骤需要手工完成。 另外,如果你与iOS provisioning profiles打过交道,一定能体会到,对它的管理和更新是多么烦人;假如有一个集中式系统,来管理所有的profiles,那能为工程师节省很多时间。 Building on push and providing daily deploys 把我们的CI机器与Jenkins服务器联动起来,安排一个计划让它每次git push操作时都进行构建,这是小菜一碟。事情非常容易。但正是这么一个简单的步骤,每个星期可以帮助我们发现几次提交时忘记了某个文件,或编译问题——通过IRC或邮件,开发人员会得到通知,这样,这类问题在被发现几分钟内就会得到解决。除了push后立即构建app,我们还提供每日构建包,任何一个Etsy员工都可以把这个每日构建包安装到他们的移动设备上——这是“吃自己狗食”的精髓。促进我们的同事安装预发布版本的简单方法就是在他们使用官方发布版本时提醒(骚扰)他们。 测试 对于iOS设备,有七种不同的iPad,五种iPhone,还有iPod,当说到Android时,那就更不用说了,简直是多如牛毛,即便是只关注主流设备也没能少到哪里去。CI的目标是:问题一旦被引入,就立即发现:我们不能依靠测试团队在每次代码提交时,都一遍又一遍的验证同样的功能特性! 在Web端,我们已经有大量的测试集,这是我们引以为豪的,而且TDD文化已经形成。基本上,我们的移动应用也借用很多web端的代码库来提供内容:数据通过API获取,而且很多页面也是web views。移动应用的大多数核心逻辑依赖于UI层,这可能通过功能测试来覆盖。正因如此,我们第一个方法是聚焦于一些功能性测试,其前提是我们的API已经在Web端被测试过了(通过单元测试和冒烟测试)。 移动应用的功能性测试并不是新鲜事情,选择性也非常广泛。在我们公司,我们用Calabash 和Cucumber。Cucumber的友好格式和预定义步骤,加上Calabash,让测试团队自己就可以写测试,非无需移动应用开发工程师的帮助。 到目前为止,我们的功能测试运行在iPad/iPhone iOS 6和iOS7 以及Android上,覆盖我们第一层级的功能,包括: 搜索列表和商店 注册新用户 用信用卡或礼品卡买东西 功能测试会模拟一个真正用户的使用步骤,所以这些测试需要某种假设的资源一定存在。比如在结算测试这个例子上,这些资源就包括: 一个专门用于测试的买家帐户 一个专用于测试的卖家帐户 一个与帐户已关联好的信用卡 而我们的结算测试包括: 用买家帐户在移动应用上登录 搜索某种商品(在卖家帐户的商铺里) 把它加到购物车 用信用卡支付 一旦测试结束,在后台就会有一个机制触发,用来取消这次交易,把该信用卡重置。 我们的功能性测试发现了bug,下面就是在iPad上的一个例子: 我们的注册测试导向了这个页面,并填写了所有的可见字段。然后,测试走到了先择 “Female“, “Male”和“Rather Not Say”这一步;在这个例子中,测试失败了(因为没有“male”选项)。

Continue reading