写点什么

实现android ci-金马国际

    2013 年 8 月 08 日

    ci 在 web-based application 上已经有了非常成熟的实现,由此也积累了大量的优秀实践。但这些实践在 android 平台中是否适用? 已知的 ci 工具是否能够很好的支持 android? android ci 是否也能即时的反馈 android application 的健康状况? 这篇文章中将通过实践,向大家展示 android ci 可用的实现方法,是时候为我们的 android app 搭建 ci 了。

    一、android ci 带来了什么?

    1) app 质量的提升

    事实上 ci 并不能直接提高 app 的质量,但是 ci 提供了对 app 的监测和反馈,通过持续的检测和反馈,可以完成对 app 的持续改进。

    在 ci 的 compilation 阶段,若出现编译失败频率较高,一是因为代码未按照原子提交的原则进行,二是本地开发环境不干净,存在与 ci 环境不一致的地方,导致每次提交时不能提交所有文件,总是需要手动挑选提交文件。

    在 ci 的 testing 阶段,若出现失败,很有可能说明此次提交已经破坏了与之相关联的功能或者模块。app 通过 testing,就是 app 可用性的一种反馈。

    ci 的 inspection 阶段会对代码做多方面的考察,如 checkstyle,单元测试覆盖率,代码静态 bug 分析等,这些都是对代码质量的检测,通过这些改善检测结果,代码质量也就会随之得到提高。

    ci 将各环节的结果反馈给整个团队,团队为改善这些结果付出努力后,app 的质量自然也就得到了提升。这也就是为什么反馈环节在 ci 是如此的重要,以至于失去它,ci 无法发挥任何作用。

    2)风险降低

    ci 将项目当前的健康状况即时的通报给整个团队,使得项目状况变得十分透明。团队成员在获得 ci 的反馈后,会关心项目的健康状况,逐步的团队所有人都习惯为 app 质量承担起自己的责任。

    bug 的出现总是不可避免的,那就希望这些 bug 尽早的出现。持续的进行 testing 可以让 bug 尽可能早的被发现。同时很快就能定位 bug 引入时间,并解决它。在发布前夕,发现 app 竟然存在部署问题,这一定会让大家变得紧张起来。持续的进行 deploy,可以让这些问题尽早的暴露出来并解决。

    3)重复步骤减少

    为了得到一个可发布的 android app,需要经历编译,测试,验证,部署等众多步骤。为了节约出包的时间,减少对资源的消耗,保证步骤都被正确执行。ci 可以帮组构建一个可以重复执行的出包流程,并通过不断的优化,缩短时间。这样一个确定的出包步骤能够让开发人员在本地环境快速的完成部署。

    4)对产品的信心增强

    项目在一个透明的环境中运行着,团队成员都关注这 app 质量,努力改善项目健康状况。这样一个齐心协力的团队状态,相信整个团队对于项目的信心也会随之增加。

    二、android ci 面临的困难

    android 环境存在不稳定。android 的模拟器在虚拟机中存在不能稳定运行的状况。

    自动化框架不够成熟。尤其是自动化测试。

    无法完成自动化部署。

    android 设备中有些性能较低。需要在这些设备上运行 function test 时资源紧缺就更加明显。

    android 正在快速发展,带来了多个差异较大版本。那 ci 就应该对这些版本都能够兼容。

    三、一步步实现 android ci

    android 上的 ci 构建链与其它平台一致,依然包含 compilation, testing, inspection, deploying 阶段,每一个阶段的 feedback 的都保持对整个团队透明。

    ci 中各个步骤执行先后顺序的安排,应该是执行时间较短的优先执行。执行时间短的一般在提交代码前就可执行,错误率也比较低,就应该尽可能先执行。这样失败会来得更早一些,每一次 ci 运行失败前验证完毕的东西更多。上图中 ci 的工作流,正是在这样的一个原则的基础上形成的。

    环境准备

    * 在 ci 服务器上安装 java 和 android 运行环境

    * 安装构建工具,本文采用 ant 进行实践

    * 搭建好 ci 服务。本文采用开源的 ci 服务 jenkins(hudson)。

    * jenkins 在功能上完全能够满足功能上的需要,且简单易用。

    * 安装 ruby 环境。本文中使用的 functional test 测试工具是基于 ruby 实现的。

    步骤 1:持续构建

    持续构建的目的是随时可自动化生成最新的可运行的 app。虽然有这么多限定词来表示这一步完成的验证条件,但事实上只需要经过三个步骤即可完成。

    一是更新代码,jenkins 中已经很好的支持了 svn 和 git 这两项常用的代码管理工具。二是采用构建脚本构建安装包,android 已经很贴心的连 ant 构建脚本都为我们准备好了,并且因为 android 的包结构的规范,也很大程度上消除各开发人员环境下项目机构的不一致。三是持续执行前两步,只有在每一次出现任何代码变动时立即执行前两步才能保证随时都可以提供可运行的安装包。

    持续构建实现起来比较容易,但是它所达成的效果还是很不错的。对开发人员来说,都可以采用同一个脚本快捷的在本地生成安装包,这在很大程度上也减少了出现“这在我机器上运行的很好”的问题。对于测试人员,随时都可以获取最新的测试包,不需要再等待开发人员腾出时间来做这件事。对于产品人员,可以利用这些最新包,在开发人员完成后第一时间获得反馈。甚至可以在完成部分功能的情况下就开始体验了。

    best practice:

    * 在每一次提交后都对整个 project 进行构建。这里的提交应该包含任何一个微小的改动。

    * 所有人遵循相同的构建顺序,采用同一套构建脚本

    * 每次构建的时候都执行同一套脚本

    步骤 2:持续测试

    持续测试是快速的通过自动化的手段收集软件健康状况的方法。持续测试是为了验证构建完成的包功能是否可用,而不仅仅能够安装运行。对 app 的测试可以从 ui, function, code 三个层次来进行,这三者间的权重关系可以参照测试金字塔来设计。

    根据前文提到的优先运行最快的原则,这三个层次的测试,应该按照 unit test, functional test,和 ui test 的先后顺序安排在 ci 执行。

    1、添加 unit test

    unit test 是运行成本最低的测试,并且对于测试用例覆盖最为全面。鼓励尽可能利用单元测试覆盖用例。java 中的单元测试首选的还是使用 junit,但 android project 的代码因为对 sdk 存在着极强的依赖,仅仅使用 junit 进行单元测试,能够覆盖的代码实在太少。为了解除对 sdk 的依赖,自然会考虑引入 mockito 这样的 mock 框架。但即使借助 mockito 写单元测试的工作量依然巨大,因为需要 mock 的对象实在太多。并且 android 的 object 在 jvm 中无法创建。

    这时可以采用 robolectric 单元测试框架,这将大幅度提升单元测试覆盖率,且理论上可以达到 100%。robolectric 是以 junit 为核心,完成了对 android sdk 的 stub。采用 stub 的方式后,android 的组件在 jvm 中即可创建并运行,无需在 android 平台下运行。这也意味着在 android 开发中可以采用 tdd 的方式,进一步提高单元测试覆盖率。该框架的使用 junit 完全一样,运行性能也一致。

    由于 robolectric 对 sdk 进行了 stub,在写单元测试时完全可以对组件状态进行验证,甚至可以对组件进行操作。下面这个测试就是对 button 点击事件的测试,并且验证了 activity 的状态。

    复制代码
    @test
    public void should_be_finished_if_clicked_on_cancel_button() {
    robolectric.application.oncreate();
    customactivity activity = new customactivity();
    intent newintent = new intent();
    activity.setintent(newintent);
    activity.oncreate(null);
    button cancelbutton = (button) activity.findviewbyid(r.id.cancelbutton);
    cancelbutton.setvisibility(view.visible);
    robolectric.clickon(cancelbutton);

    接下来的工作就是将 robolectric 集成到 ci 中,让它检查程序的健康状况。robolectric 本质上还是 junit,只是多了一些 stub 对象而已。那我们集成 robolectric 的方法和 junit 完全一致。只需创建 ant task,并在 jenkins 中执行此 task 即可。此 ant task 如下:

    复制代码
    <target name="unit-test" depends="clean, init, compile">
    <junit fork="yes" dir="." failureproperty="
    test.failed" haltonerror="false" haltonfailure="false"
    printsummary="yes" forkmode="perbatch"
    showoutput="no">
    <classpath location="${cobertura.instrumented.dir}"/>
    <classpath>
    <pathelement path="${tested.project.dir}/bin/classes"/>
    classpath>
    <classpath location="${classes.dir}"/>
    <classpathrefid="test.classpath"/>
    <formatter type="brief" usefile="false"/>
    <formatter type="xml"/>
    <batchtesttodir="${reports.xml.dir}">
    <filesetdir="${src.dir}">
    <include name="**/*test.java"/>
    <exclude name="**/alltests.java"/>
    fileset>
    batchtest>
    junit>
    <junitreporttodir="${reports.xml.dir}">
    <filesetdir="${reports.xml.dir}">
    <include name="test-*.xml"/>
    fileset>
    <report format="frames" todir="${reports.html.dir}"/>
    junitreport>
    <fail if="test.failed" message="unit test(s) failed."/>
    target>

    在将这些测试集成至 ci 后,最重要的一步收集结果是不能忘的。之前已经说过 calabash 也可按照单元测试报告规范输出,加上 robolectric 本身就是 junit 框架的扩展,报告也是按照单元测试报告规范输出。unit test 和 function test 的报告即可使用 junit test 收集。

    要想获得单元测试覆盖率报告,cobertura 是个不错的选择。添加

    复制代码
    <target name="coverage-report">
    <cobertura-report srcdir="${tested.project.src}" destdir="${cobertura.coverage.xml.dir}" format="xml"/>
    target>
    <target name="summary-coverage-report">
    <cobertura-report srcdir="${tested.project.src}" destdir="${cobertura.coverage.summaryxml.dir}" format="summaryxml"/>
    target>
    <target name="alternate-coverage-report">
    <cobertura-report destdir="${cobertura.coverage.html.dir}">
    <fileset dir="${tested.project.src}">
    <include name="**/*.java"/>
    fileset>
    cobertura-report>
    target>

    从 jenkins 上即可获得清晰的单元测试覆盖率的报告

    2、添加 function test

    android 为大家提供了一套集成测试框架 android integration testing framework。但此框架未集成 cucumber,这导致每增加一个 function test 都需要较大的开发和维护工作。这样高成本的实现 function test 将大大延缓开发进度,最终因为项目进度的原因导致 function test 被丢弃。产生这样的后果那必然是不愿意看到的。

    目前 android 平台下已经出现多种 functiong testing 测试工具,如 native driver, robotium, calabash 等。在尝试对比后,最终选择了 calabash android 作为金马国际的解决方案。calabash android 是 cucumber 在 android 平台的实现,使用 ruby 书写 function test,并提供了一组操作 anadroid app 元素的 api。

    calabash android**** 的主要优势有以下三点:

    a. 对于 bdd 的支持

    b. 使用 ruby 实现 function test 更加的符合自然语言的习惯。使得 qa 也能轻松的实现 function test

    利用 calabash 提供的对 app 组件操作的 api,实现启动 app 并登陆只需要以下短短的几行代码:

    复制代码
    given /^i launch and login telstraapp$/ do
    step %q|i launch the app|
    step %q|i wait to see "my account login"|
    step %q|i enter "#{$username}" into input field number 1|
    step %q|i enter "notarealpassword" into input field number 2|
    step %q|i press "login to my account"|
    end

    c. 支持 android 和 ios 使用相同的 api 操作 app。使得 ios 和 android 平台中的 feature 可以重用

    jenkins**** 集成 calabash android

    运行 calabash android 需要 ruby 环境,同时也建议安装 rvm。在 ci agent 上安装 ruby 和 rvm,并为 jenkins 安装 rvm plugin 后运行环境就准备好了。

    在 jenkins 中执行运行 calabash android 的 shell 命令前需要注意指定运行时的 gemset

    calabash android 在 jenkins 中的执行命令如下:

    calabash 在运行完毕之后,可以按照单元测试报告的规范提供测试报告

    3、添加 ui test

    android 在新近退出了 ui 测试工具 uiautomator。此工具仅支持 android4.1 及以上平台,鉴于目前市场上 2.3 和 4.0 版本仍占主导的情况来看,目前还无法满足大家的需要。另外应用该工具实现 ui 测试的开发成本还较高,笔者暂不推荐使用此工具,但应该关注其发展。

    另外基于录制回放机制的测试方法同样可以进行 ui 测试。但录制回放的方法在面对功能快速迭代时,维护工作会急剧增加,而这个维护成本可以说是很难承受的,所以在此也不会将这种测试方法集成至 ci 中。

    目前来看 android 中 ui 测试还无令人满意的方法。若对 ui 成功比较看重,可以投入精力应用 uiautomator 进行 ui 测试。

    best practice:

    * 将测试按照单元测试,组件测试,功能测试和系统测试进行划分。单元测试应该在每次提交时触发执行,其它的测试根据运行时间长短和重要程度可以每次提交触发执行或者定时周期执行。

    * 将运行较快的测试优先执行。

    * 让功能测试能够重复执行。否则维护成本太高,会被舍弃。若是后台数据导致不可重复,可以将数据抽象成为数据集,在每次运行前进行重置。

    * 书写测试时每一个 assert 只做一种判断,这样可以明确每次测试的目的,并且可以快速定位测试失败愿意。

    步骤 3:持续检查

    持续检查是对于代码本身检测和反馈。检测主要通过对代码静态分析验证代码风格,编程规范,代码复用,代码语言中的 best practice 等多个维度的代码质量。

    sonar 作为一个开源的代码质量检测工具,涵盖了 7 项代码质量检测方式。这充分满足 android 平台下对于代码质量的检测分析。sonar 分为两部分一部分是代码分析工具,另一部分是数据分析展示的 server。

    sonar 可进行的分析维度在其 dashboard 中可以看见:

    sonar 的分析工具也有多种运行方式,可以由 ant script, jenkins plugin, jar 等多种方式运行,为了简化 jenkins 的配置,本例子采用 ant script 的方式运行:

    复制代码
    <target name="sonar">
    <taskdef resource="org/sonar/ant/antlib.xml">
    <classpath path="cisetup/sonar-ant-task-2.0.jar"/>
    taskdef>
    <sonar/>
    target>

    best practice:

    * 将测试覆盖率,代码分析结果透明化

    * 持续降低代码复杂度

    * 持续的促进设计的演进

    * 持续的维护代码结构

    * 持续减少代码重复

    步骤 4:持续部署

    由于 android app 采用用户手动从 appstore 自行下载安装的方式发布,使得 android app 无法直接部署至用户手机中。另外 appstore 需要对于上线的 app 进行审核,不能持续进行 release。因而 android 中持续部署将以持续发布可安装包为目标。

    在以上目的下,只需根据自身项目资源找到合适的安装包管理工具即可。如本文采用 dropbox 来管理所有安装包。

    dropbox 作为一个云存储平台,在 android 终端设备上可以轻松下载存放在其中的文件,同时上传安装包也可以交由 dropbox 自己完成。

    复制代码
    name="copy-prod-apk-to-dropbox" >
    message="copying ${basedir}/bin/androidapp-prod-${version_number}.apk to
    ${dropbox.dir}/androidapp/r4/${version_number}/" />
    file="${basedir}/bin/androidapp-prod-${version_number}.apk" todir=&
    quot;${dropbox.dir}/androidapp/r4/${version_number}" overwrite="true" />
    {1}
    {1}
    best practice:

    * 保证任何时间都能够发布可用的程序

    * 为每一次 build 打上 build 号

    * 执行部署签运行所有的测试

    * 保证部署失败都能执行回滚

    步骤 5:持续反馈

    反馈是所有改进的开始,必须要让所有人获取到他们所关心的反馈信息,才能实施改进。持续反馈的目的就是让所有人都掌握项目健康状况。项目所有人事实都是有意愿知道项目当前的健康状况的,那 ci 就应该将项目的情况做到透明,并将不同的反馈通知到各相关的成员。

    ci 不同阶段产生了不同维度的反馈,如单元测试报告,测试覆盖率等。本实践中将这些反馈都透明的展示在项目金马国际首页中。之所以没有将这些反馈再以邮件的方式通知所有人,是因为团队成员已经养成了查看 ci 的习惯。

    如果说只给所有人发一封邮件说明项目状况,那必然是告诉所有人“ci 所有步骤是否都返回正确?”。这样一个反馈,包含了编译正确,所有测试通过,安装包已经准备完毕等重要信息。有必要让所有人都知道这个信息,特别是在 ci 执行失败的时候。jenkins 自身已经提供一个简单有效的透明化方法,以项目为蓝色表示通过,红色表示有步骤失败。

    反馈的通知方式有很多种,不一定要采用邮件通知的方式。可以寻找更加有趣的方式,如果播放音乐和设置警报灯。在每一次 build 成功或失败后都播放一段有趣的音乐,打开不同颜色的警报灯,这两种方法都是是一种简单有效的方式,可以让项目所有人都获取到最为关键的信息。

    best practice:

    * 向所有团队成员公布 ci 反馈

    * 形成持续的发布 ci 反馈的机制

    四、结束语

    从本文的实践来看为 android 项目搭建 ci 与其他类型项目步骤基本一致,所不一样的是各步骤中依赖的实现技术而已。确实也因为 android ci 所依赖的技术的不够成熟,存在一些支持不足的情况,如对 ui 的测试,影响了 ci 的价值。但它 ci 的价值依然值得花时间去搭建。

    作者简介

    朱傲,北京交通大学硕士,现为 thoughtworks 咨询师。关注软件质量、移动开发、知识管理等领域,喜欢技术写作及分享。


    感谢对本文的审校。

    给infoq 中文站投稿或者参与内容翻译工作,请邮件至。也欢迎大家通过新浪微博()或者腾讯微博()关注金马国际,并与我们的编辑和其他读者朋友交流。

    2013 年 8 月 08 日 09:427131

    评论

    发布
    暂无评论
    • 由 于instrument test使用和运行的不便,在android项目中对代码添加测试变得非常困难。本文基于项目实践,描述了在实际项目中如何借助于mvp模式和 robolectric框架,实现逻辑和视图的分离,为代码添加有效完备的单元测试,并简单介绍了robolectric的实现原理以及如何对其进行扩 展。

    • 围绕自动化单元测试实践,讲述关键组成部分、原则、流程,并以团队实践作为案例,透彻、清晰地阐述了整个流程的质量活动,并结合实施前后做了对比,让团队、管理层看到是要质量,还是数量的问题。

    • 持续集成理念经过10多年的发展,已经成为了业界的标准。而对于ios领域来说,因为技术本身相对比较年轻和苹果与生俱来的封闭思想,在持续集成方面的发展相对滞后一些,但是,随着越来越多的ios开发者的涌入,以及各个互联网巨头加大对ios开发的投入,诞生了一大批非常好用的持续集成工具和服务,本文的目的就是介绍一下如何有效的利用这些类库,服务快速构建一个ios开发环境下的持续集成平台。

    • 专栏上一期,绍文讲了编译插桩的三种方法:aspectj、asm、redex,以及它们的应用场景。学完以后你是不是有些动心,想赶快把它们应用到实际工作中去?

      2019 年 2 月 23 日

    • 持续集成(continuous integration,ci)这项基本的xp实践现在已经变成了被广泛使用的开发者最佳实践之一。infoq为您提供了“持续集成:改善软件质量并降低风险”一书中的“第六章:持续测试”,在这一章中,作者提出了一些编写优秀测试以保证系统质量的建议和示例。

    • 时下大多数开发人员对持续集成(continuous integration,ci)的基本原理已经很熟悉,但是他们中只有一小部分人能够从优化ci设置中彻底受益。本文将讨论如何把持续集成由一个名义上的定时作业,变为开发活动中一个有效、而且能提高生产力的“中枢”。

    • 持续开发的基本原则主要包括两条:一是,规范化、自动化核心步骤;二是,快速反馈,增量开发。

      2019 年 9 月 2 日

    • 通过研究一些开源项目的开发,你能从中学习到一个优秀项目对软件工程的应用,加深对软件工程知识的理解,进而应用到项目实践中。

      2019 年 6 月 11 日

    • 现在,已经有大量的android自动化测试架构或工具可供我们使用,其中包括:activity instrumentation, monkeyrunner,robotium,以及 robolectric。另外lesspainful也提供服务来进行真实设备上的自动化测试。

    • 在今天的分享中,我与你介绍了如何通过travis ci,为我们的项目引入了持续交付能力。

      2019 年 10 月 3 日

    • 今天这篇文章要达到的目的是,帮助你完成flutter开发测试环境的安装配置。

      2019 年 7 月 1 日

    • java世界最普遍的测试框架junit即将迎来更新。没错,完全重写的junit 5解除了“junit平台”与“junit工具”之间的绑定,使得该平台可用于其他测试框架,借此进一步重新定义了jvm测试的未来。不仅如此,该版本中api也有了进一步发展,并提供了大有前景的扩展模型。

    • 在完成了本专栏的三篇文章之后,我感觉自己突然陷入了对敏捷测试的一些不同的思考。敏捷本无定式可循,不同的组织由于文化、产品和用户等的不同,在敏捷的具体做法上自然存在很大的差异——这本是敏捷实施中常态,但最近接触到不少敏捷组织中的测试管理者和测试工程师,大家对于如何在敏捷中做“好的测试”往往争执不下,尤其对于自动化测试,看法更是差异巨大。

    • 2015年11月,thoughtworks发布了新一期的技术雷达。技术雷达是以独特的形式记录thoughtworks技术顾问委员会对行业产生重大影响的技术趋势讨论的结果,为从cio到开发人员在内的各方利益相关者提供价值。这期雷达的技术趋势主要体现在:受到热捧的微服务相关技术,逐步成熟的以docker为典型的容器化生态系统,备受企业和用户关注的信息安全问题。本文就从这几个新趋势来分析一下给软件测试带来了哪些影响。

    • fastlane是一组工具套件,旨在实现ios应用发布流程的自动化,并且提供“一个运行良好的持续部署流程”,只需要运行一个简单的命令就可以触发这个流程。本文采访了fast lane的创造者felix krause。

    • 测试覆盖着整个软件开发的生命周期。从需求评审开始,测试工程师就要介入了解需求,并编写用例;开发过程中,研发工程师们会针对测试用例,编写单元测试;开发完毕后,测试工程师介入进行集成测试;上线后还需要进行线上跟测,追踪bug。所以可以看出,测试对于软件的重要性不言而喻,毕竟可靠的质量才是产品的基本竞争力。

    • 本文是steven lemon所在团队遇到的手工编写自动化ui测试的问题总结与反思,希望给广大开发者借鉴和启发。

    • 最近在ibm developerworks发表的一篇文章中谈到如何使用开源工具将构建过程中的持续集成(continuous integration,ci)和代码检查这两项任务自动化。它描述了如何安装和配置hudson,并使用subversion、ant对hudson进行配置。同时辅之诸如findbugs和pmd之类的软件检查工具,来创造一个可以对测试结果和缺陷情况进行持续性反馈的构建过程。

    • 这一讲介绍持续集成和持续发布,以及web全栈项目中常见的测试维度。

      2019 年 11 月 18 日

    发现更多内容
    网站地图