客服电话:15682930301

计算机网络论文

当前位置: 毕业论文>计算机论文>计算机网络论文 > 正文

一种安卓人机对战测试的框架及评价方法

发布时间:2019-12-08 18:48文字数:18920字

  摘 要:近年来,在互联网的带动下,使用智能手机的人数越发上涨,安卓与IOS也成为了移动应用的主体。其中安卓平台的发展更为迅速,2017年11月1日,美国市场研究机构NPD Group表明,安卓在美国智能手机市场上的销量份额首次超过IOS。也正因如此,市场对安卓应用的要求不断提升,开发人员需要提供高品质的应用才能满足用户的需求,对安卓应用的测试便成为了开发应用中的一个关键流程。

  但是,测试结果本身的好坏,其实是难以判断的。所有的测试用例都通过了,并不代表应用的所有问题都被发现了,更何况,使用不同的测试方法,由于他们的测试过程不同,所得到的结果也会完全不同,这就使得这些结果相互之间不可比较。因此,为了使得安卓测试的结果有一定的可比性,本文提出了一种能够标准化安卓应用测试结果的框架,主要用于在人机对战中能够给出一个可比的结果。本平台通过代码的各种覆盖率、日志中的异常信息、变异的执行情况等方面进行结果的评判,最后给出所有的详细生成结果与最终得分。与此同时,使用该框架中接入的工具对多个安卓应用进行测试,得出的结果与已有论文中所述的结果基本相同,证明了框架的可靠性。

  关键词:安卓测试;自动化测试;人机对战;安卓变异

  前 言

  现代智能手机系统中,安卓框架与IOS框架几乎平分了整个市场。而安卓应用由于编写简单,发布也不像IOS一样需要进行细致的审核,因此安卓应用的数量在近年来也在快速攀升,由于相同功能的应用数量不断增加,这也就导致市场对安卓应用的要求越来越高。因此,对安卓应用进行测试的重要性也跟着水涨船高。但是,安卓应用的测试结果并不能得到一个准确无误的评价,因此,想要开发出高品质的安卓应用,不可或缺的 一个环节就是需要能够得知对应用的测试是否完善。

  由于安卓应用的代码基本是基于Java完成的,因此,对安卓应用进行单元测试是比较传统的测试方法之一,这种方法与Java代码测试相似,它的历史沉淀也较深,因此对测试结果的优劣判断也能给出一个比较准确的答案。而近年来,有一种新的测试方法正在逐渐发展,就是安卓UI测试。这种方法主要模拟了真人对已经编译好的应用进行使用时的情况来进行测试,因此更加能够暴露出一些代码之外的问题。

  本文中所述的框架,就是针对安卓UI测试来及进行编写的,它可以自动执行人工编写的代码,或者自动化接入的工具来进行安卓UI测试,并给出这一次测试的结果好坏。本文完成的主要工作及创新点有:

  (1)使用ASM技术,研究安卓.class文件的修改方法,找到一个切实可行的安卓文件插桩方法,并能够将数据反馈。使用Java与Appium技术,完成安卓测试代码的自动化执行。同时为各种安卓自动化测试工具编写一个接口,使得可以将其接入并统一执行。

  (2) 进行安卓源代码的变异生成,并通过框架进行变异测试,以得到测试信息。同时研究算法对测试结果进行评分,得出标准化的、误差小的测试结果判分,用于对不同的测试之间进行相互比较。

  (3)本文完成的框架,据编写本文前的调查,是首个对安卓测试结果进行规范化评分的平台。

  同时,本文也进行了充分的实验数据分析,来保证框架给出结果的可靠性。

  第一章 绪论

  本章首先介绍了UI(User Interface)测试与安卓UI测试技术的研究背景和意义,其次简单介绍了一种人机对战框架的实现流程与使用方法,并讲述了本文做出的主要贡献与主要工作以及创新点,最后,阐明了本篇论文的组织结构。

  1.1 研究背景以及意义

  UI测试是安卓测试目前一个重要的组成部分。相对于传统的,注重于对代码逻辑进行测试的单元测试,UI测试更加注重于逻辑之外的部分,如用户体验、界面交互等等只有在UI层才能被发现的问题。

  UI测试的概念是由敏捷大师Mike Cohn所提出,然后由Martin Fowler大师在此基础上提出了测试分层概念,以区别于传统的自动化测试。相较于单元测试与接口测试的内容主要侧重于代码的检查,UI测试的对象UI层是用户使用产品的入口,所有的功能都是通过这一层提供给用户的,也就是说,进行UI测试,是需要以用户的视角来对应用进行整体的,不仅仅是功能,也包括了用户体验的测试。

  Google的自动化分层投入占比分别为:单元测试70%、接口测试20%、UI测试10%,UI测试的投入明显小于前两者,这也是无可厚非的,因为作为软件,最重要的当然是功能性,其次才会考虑到用户体验。但是,在软件版本快速迭代的这个时代,进行UI测试的速度远快于另外两者,这也就导致很多小型公司出于经济与时间的考虑只注重于UI测试,因此,UI测试仍然是不可或缺的。

  虽然UI测试由于自动化的发展,可以极大程度的节省时间,不过自动化UI测试仍然有以下问题等待解决:

  (1)平台繁杂:业界的UI测试框架十分之多,使用较为广泛的有UIAutomator、Robotium、Appium等,但是不同的框架几乎都只能对应一种或数种平台与语言,而应用本身可能是由不同的语言所编写的,这就要求测试者需要掌握多种框架的使用方法,较为耗时。

  (2)维护困难:UI测试顾名思义是针对UI的测试,但是如果在测试代码完成后,仍然对UI进行了修改,那么测试代码也就需要进行相应的修改,而且很有可能是大量的修改。

  (3)稳定性因素:测试代码本身很容易受到环境因素的影响,如网络、手机性能等等,又或者待测应用本身就不稳定,那么相同的代码、相同的应用,在不同的测试中也会导致不同的结果。

  安卓UI测试与普通UI测试几乎没有什么不同,除去平台已经被确定以外,维护和稳定性仍然是两个比较重大的问题。这也是未来本文应当着重于研究的目标。

  而当安卓UI测试可以稳定进行后,我们就应该思考下一个问题:测试的效果究竟如何?在我们终于克服了万难,让测试代码可以稳定执行之后,这份代码真的能够起到一个非常好的效果吗?这很难界定,因此,本文观点认为需要指定一份标准来评判这次测试是否达到了良好的效果,这也就是人机对战框架被编写的原因了。

  1.2 框架概述

  为了评估某一次安卓UI测试代码的效果,我们需要设定一些依据来给出评分。但是如果只有一份测试代码,就算得到了分数,无法与其他事物进行比较,还是不可知其效果。考虑到安卓UI测试的方法可以分为两种:人工编写自动化测试脚本与自动化测试工具测试。本文认为,可以将自动化工具测试设定为一个固定项目,这样一来,即便是单份的代码,也可以和自动化工具进行测试的结果进行比较,从而判断人工代码的优劣。

  本文所述的人机对战框架,面向使用Appium框架的测试代码,这是一个兼容性很广的框架,也是本文选取它的原因。框架通过ASM对安卓应用源代码进行插桩,保证在安卓应用测试完成后,可以返回这次测试所经历路线的各种细节,从而得到本次测试对源代码进行的覆盖程度。同时,在测试过程中,框架会截取使用的自动化工具本身记录的信息、Appium的日志信息与手机的adb日志信息。最后对他们进行分析,通过算法来计算出测试的最终得分。除此之外,框架可以通过安卓应用源码生成该应用的变异,接着用相同的代码对这些变异进行测试,根据代码的通过率来判断变异杀死率,同样作为可选的得分项之一。

  1.3 本文主要工作以及创新点

  本文主要以人机对战框架为研究对象,讨论人机对战框架的实现原理、使用方法与框架所使用的各种技术的原理。主要工作以及创新点如下:

  (1)学习ASM接口,了解Java文件编译后生成的.class文件的修改方法,使用Jacoco作为插桩工具,对安卓代码进行插桩,并在测试结束后返回数据。学习Appium框架,使得安卓测试代码在提供相应的输入后能自动进行执行并得到分数。同时为各种安卓自动化测试工具编写了接口,只要将特定的工具接入该接口则可以通过统一化的指令使得该工具能够一键执行。

  (2)了解安卓代码的变异方式,使用MDroid+作为安卓应用的变异生成工具。同时通过计算测试完成后返回的信息中分析出的覆盖率、异常信息、变异杀死率等数据,得出标准化的、误差小的测试结果判分,用于对不同的测试进行相互之间的比较。

  (3)本文完成的框架,据调查,是首个对安卓测试结果进行规范化评分的框架,相信能够给学术界的其他研究者带来一定的启发。

  1.4 本文组织结构

  本文共分为五章,各个章节安排内容如下:

  第一章:绪论。本章主要介绍了该课题的研究背景以及研究意义、UI测试与安卓测试的详情与未来发展、本文的主要工作内容与主要创新点和本文的组织结构。

  第二章:安卓UI测试技术的概述。本章详细阐明了安卓UI技术目前的难点与测试方法,对比了各种测试技术的优缺点。

  第三章:人机对战框架的需求分析与概要设计。

  第四章:人机对战框架的详细设计与实现。

  第五章:人机对战框架的评分标准与对比实验。

  第六章:全文总结,提出现在框架的不足与展望。

  第二章 安卓UI测试技术

  本章详细阐述了安卓UI测试技术的难点,以及两种安卓UI测试的方法,并对他们进行比较,从而讨论安卓UI测试的发展方向。

  2.1 安卓UI测试技术简介

  安卓测试与传统的Java测试并不完全相同,由于安卓程序的部分代码是Java代码,因此对于这些部分的测试,我们可以选用和Java测试相同的单元测试。但是仍然有一部分代码并不是Java格式的,因此,这些部分就变得难以测试了。并且,在整个程序都被完成后,只是进行单元测试或是集成测试,也只能够观察到代码本身是否出现问题,而无法关注于用户体验这一同样重要的方面[1]。

  因此,测试者们选取使用UI测试来补足。安卓UI测试是指,测试者模拟使用者的行为,对安卓程序编译后产生的实体进行操作,来观察安卓程序的反应,以达到测试目的的一种测试方法。由于程序终究是为了使用者诞生的,这一方法以使用者作为主视角代入,不仅能够找到安卓程序在代码上是否存在问题(即BUG),也可以体现出安卓程序在用户体验上是否出现了缺陷。

  安卓UI测试的实现主要分为人工测试与自动化工具测试。这两者分别在本文的2.3与2.4中进行介绍。

  2.2 安卓UI测试的难点

  安卓UI测试虽然有效,但仍然存在诸多问题。最大的问题有如下两个:

  (1)测试代码难以通用:

  由于安卓系统的碎片化[2],导致代码在不同型号、不同版本的手机或模拟器上,未必能够通用。很有可能在这台手机上能够稳定运行的代码,在另一台手机上运行会在一开始就因为权限问题或存储问题等原因就执行失败了。

  (2)代码编写较为繁琐:

  UI测试的代码编写消耗的人力依然巨大,实际使用中一个简单的点击,或是拉取动作,就要用一条较长的代码来实现。即使能够通过一些UI测试工具来简化,整个代码的编写流程依然耗时远超过实际手动操作。

  2.3 安卓UI测试的方法

  安卓UI测试在现今主要有两种方式:人工编写代码测试与自动化工具测试。

  2.3.1 人工测试

  安卓UI测试的人工测试主要也可以分为两种方式:

  (1)直接编写测试代码

  现如今,安卓UI测试的代码编写都是基于框架编写的[3],而最为流行的安卓UI测试框架,有UIAutomator、Robotium与Appium[4]。

  这三者分别使用不同的代码规范,测试者需要按照这些规范编写测试代码并运行,就可以模拟出正常用户使用安卓应用的行为。

  (2)众包测试

  众包测试[5]是一种比较新型的测试方式,它会将待测的安卓应用分发给数百人即进行测试,这些人会分别使用他们自己能想到的所有方法来测试该应用,并填写测试报告。相较于编写测试代码而言,这种方式并没有太好的复现性,但是由于用户数量巨大,而且测试者和编码者不同,可以完全将自己看为程序的使用者来提出意见,因此也能得到好的反馈。

  2.3.2 自动化工具测试

  为了节约人力与物力,研究人员们意图开发出能够自动进行安卓UI测试的工具,并取得了不错的进展。现如今的自动化工具,主要可以根据其测试策略分为三类:随机探索策略、基于模型的探索策略与系统的探索策略[6]。

  (1)随机探索策略

  随机探索策略[5]顾名思义,即所有的操作都是随机的,自动化工具会根据自己的随机策略为安卓应用生成输入。并且,该策略几乎只能应用于UI测试。这种方法生成测试用例的效率实际上并不高,因为随机性,经常会生成重复的应用输入。同时,由于安卓应用的层次性,即许多页面需要进行一定量的固定操作才能进入,随机探索策略就难以进入这些页面了。但是,由于随机策略生成测试用例,即应用输入的效率十分之高,该策略也常用来进行安卓应用的压力测试。

  比较有名的随机探索策略自动化测试工具有Monkey与Dynodroid,前者是由安卓SDK(Software Development Kit)自身提供的命令行工具,可以说是官方工具了,正如其名,它会像猴子一样随机的进行动作来测试。后者则是一个年代较为久远的工具,作为安卓自动化工具的开山鼻祖之一而常常被人提及。

  (2)基于模型的探索策略

  基于模型的探索策略[7],是从一些网络爬虫中得到的灵感,这种策略的自动化工具,会获取每个页面中,可以点击或进行其他操作的控件,将他们放入栈或队列中,接着按照顺序一个个进行处理。当然,也可以通过配置文件来说明不需要进行操作的活动的名称来加快进程。这种方法的优点是可以将所有的UI都测试到,无论深度有多深,这是随机策略无法达到的。但是同样的,基于模型的探索策略也有缺点,就是在不改变核心算法的情况下,每一次的测试路径是一样的,如果针对一款刚刚完成的安卓应用进行测试的话,每当遇到让程序崩溃的错误,就需要重头测试一遍,较为消耗时间。

  比较有名的基于模型的探索策略自动化测试工具有AppCrawler与PUMA,前者是国人编写的一款工具,通过配置文件的输入,可以做到自动辨识登录界面,从而做到能够测试绝大部分应用。而后者则是一款基于Monkey编写的框架,他可以用来接入安卓应用从进行动态分析。

  (3)系统的探索策略

  有一部分安卓应用的行为,只有在提供特定的输入的时候才会显现出来。而之前的两种方法显然无法做到这一点,因此,一些安卓测试工具使用了更复杂的技术,比如符号执行或进化算法来对尚未覆盖的代码进行探索,这就是系统的探索策略[8][9]。

  由于这一研究较为深奥,因此相关人员对于这部分工具也仍然只是处于研究中。较为有名的系统的探索策略的自动化测试工具有ACTEve,一种符号测试的工具。

  (4)UI测试工具的对比

  2015年,Shauvik Roy Choudhary等人对部分著名的安卓自动化UI测试工具进行了一次对比实验[10]。实验的结果令人惊讶,测试完安卓应用后,代码覆盖率最高的工具,居然是随机探索策略的Monkey,平均覆盖率达到了近50%。但这个数据相较于人工编写代码而言,是很低的,因为人工编写代码的覆盖率,几乎是能覆盖整个应用的。

  不过,自动化测试工具也有他的优点,那就是速度快。他们到达代码覆盖率的时间几乎都在5-10分钟内。而如果换成人类进行测试代码的编写,可能需要花上好几个小时。

  2.4 安卓UI测试的评价标准

  安卓UI测试的结果评估指标很多,对于代码而言,有代码覆盖率、异常触发率、变异杀死率等等。

  (1)代码覆盖率

  代码覆盖率应该是现如今最为常见的测试结果评估指标了,测试代码覆盖了整个应用的多少,至少从数量上可以侧面反应这一次的测试是否全面。因为测试代码最主要的目的就是通过输入来实现对程序功能以及代码的覆盖,来观察程序的反应以找到程序的缺陷,从而才能对程序继续进行改进。代码覆盖率主要包括源代码的覆盖率与中间代码的覆盖率,源代码覆盖率更为准确,但是大部分情况下,应用并不会开放源代码,因此中间代码覆盖率的适用性会更强。

  (2)异常触发率

  异常触发率对于人工代码而言可能不是必须的,因为一般触发了异常会导致程序退出,接下来的代码就无法继续执行了,可是对于自动化工具而言,这是一个比较重要的指标。工具触发了异常,说明工具找到了应用程序的问题,侧面就能表明自动化工具达到了目的,因此也是一个重要的测试结果评判标准。

  (3)变异杀死率[11]

  变异是一个比较不常见的概念,在通用的情况下,变异是指,源代码的某一处逻辑发生改变,导致程序整体功能也因此而改变的情况,一般是在开发人员手误写错了代码的时候发生。比如将大于号错写成小于号,就会使得程序的输出与预计不符。

  在这种情况下,测试代码应当使用断言来对这些部分进行检测,断言是指,对某一个输出或者某一个值做出一个固定的判断,如果程序执行导致断言处时,该值与我们设定好的固定值不同,则直接执行停止并报错,以便于发现错误。

  目前的一些测试竞赛中,会加入故意注入变异的方式,来评判测试代码能否发现这些变异。

  在安卓UI测试中,由于测试过程与单元测试完全不同,本文认为变异能否杀死,取决于运行同样的代码时,在无变异的情况下能成功执行的测试代码,在变异后的应用下能够仍然执行成功,如果执行失败了,则说明杀死了该变异,反之则没有检查到该变异。

  2.5 本章小结

  安卓UI测试相较于Java测试而言,还相对并不成熟。人工编写代码进行测试虽然可以达到较高的代码覆盖率,但是相对而言耗时较长。而自动化测试工具虽然用时很快,但是并不能够达到一个非常高的代码覆盖率值。

  由于安卓本身的碎片化问题,测试这一过程就需要考虑到许多针对特定版本的情况,这也就使得安卓测试比起其他测试而言更加复杂。

  况且,安卓框架也在不断进行更新,这也就意味着,如果不对测试代码,或自动化测试工具进行持续不断的维护,就会产生引版本更新而无法使用的情况。本文认为,现在的安卓UI测试仍然有可以发展的空间。对于人工测试,我们可以制作将人手动点击的过程录制下来并转换成代码的工具。这种工具据本文所调查,已经有人正在研究。而对于自动化测试工具,更加全面的考虑可能发生的问题,如对于登录页面,我们可以提供输入,如果遇到了让程序崩溃的问题,我们也可以通过工具复现来解决等等。总而言之,安卓UI测试目前还并不完善,但是如果能结合上述所说的各种方式,相信未来的安卓UI测试一定会变得简单而又高效。

  第三章 人机对战框架的需求分析与概要设计

  本章通过UML绘图的方法,讲述了人机对战框架的需求分析与概要设计,经由用例图、类图、时序图等内容,具体的描述了人机对战框架的设计思想与使用实例。

  3.1 人机对战框架项目的整体概述

  为了能够使得安卓应用测试结果得到一个标准化的评判,本文选择自己实现一个框架来达到这样的效果。但是人机对战框架的功能不仅限于此,人机对战框架的功能主要是为了慕测信息科技有限公司的主平台所用。慕测主平台主要分为教育版与企业版,在教育版的安卓应用分项中,学生通过提交安卓应用测试脚本来获取分数,而通过人机对战框架,可以简化原本的流程。同时,该框架也用来进行人机对战比赛这一活动。在2017年全国大学生测试大赛的安卓人机对战分项赛中,已经使用过该框架的雏形,接着将对框架进行了不断的改进以增加新的功能。

  人机对战框架的具体实现由五个部分组成:源代码修改、接收待测应用与测试代码并执行测试、反馈测试中获得的数据、对数据进行分析并计算得分、将所有详细结果以及得分输出。这五个部分会分别在第四章中进行详细说明。

  同时,据我所调查的,本文所介绍的人机对战框架,不仅是第一款用来进行人机对战的框架,也是第一款意图于将安卓应用测试结果规范化的框架。并且,最重要的一点是,框架的使用方法十分简单,只需要提供规定好的输入并修改几句配置文件,整个框架的流程就能一键完成了,也就是纯黑盒完成。

  本章中,先进行框架的需求分析与概要设计的介绍。

  3.2 系统需求分析

  3.2.1 用例图

  3.2.2 用例描述

  3.4 本章小结

  本章分为三个部分,分别是人机对战框架项目的整体概述、人机对战框架的需求分析部分和人机对战框架的系统概要设计部分。第一部分描述了人机对战框架的编写目的,使用场景以及使用案例。第二部分从用例图、用例描述、系统顺序图以及非功能性需求的角度讲述了人机对战框架的需求分析内容。第三部分则从整体上描述了人机对战框架的结构设计,通过类图和包图展现了框架的实现方式。

  第四章 人机对战框架的详细设计与具体实现

  本章通过介绍具体代码的方法,讲述了人机对战框架的详细设计与具体实现,通过对插桩、执行测试、计算数据等核心代码进行讲解来说明人机对战框架的具体运行过程。

  4.1 框架子模块概述

  人机对战框架的流程几乎是线性的,图4.1展示了人机对战框架的工作流程,编写时将框架分为三个大模块,分别是:Execution、Interface和Mutant Manage. 每一个模块都分工管理框架的不同部分。接下来本文会先简略的介绍每个模块,而关于每个模块的详细设计与实现会在4.2节中进行具体描述。

  4.1.1 Execution模块

  Execution模块顾名思义,作用是执行人工脚本测试与自动化工具测试,获取返回的覆盖率、异常触发率等数据并计算得分与给出最终结果。同时,该模块也是人机对战框架的主要流程。模块会先接收用户输入的信息,接着使用函数:

  public void MnualTest.run()

  方法来开始人工脚本测试,或使用

  public void AutomatorTools.start()

  方法来开始自动化工具测试,这两个方法会自动打开所需的后台软件,接着在连接的手机上开始测试,直到测试完毕或者测试因为某些情况而中断时结束。当测试完毕后,框架会调用函数:

  public void Feedback.getEC()

  来获得测试的覆盖率数据与异常触发情况,并直接生成到用户先前输入的输出文件夹中,因此没有返回值。接着使用函数:

  public void CulculatePoint.generate()

  方法,从之前获得的覆盖率数据生成清晰可见的HTML文件与XML文件,其中包含了详细的代码覆盖率信息。最后使用GetPoint类中的函数:

  public double GetPoint..getHTMLPoint()

  public double GetPoint.getExceptionPoint()

  两个方法来分别获取覆盖率分数与异常触发分数,就可以作为最终得分的依据了。

  4.1.2 Interface模块

  Interface模块主要与自动化测试工具相关,由于自动化工具的数量十分的多,我们不可能做到一一去为它们进行配置来使其可用,因此,编写了两个抽象类作为接口:AutomatorToolsDriver作为自动化工具的管理器,AutomatorToolsTest作为自动化工具的生成器。为了实现同时在多台手机上进行测试,AutomatorToolsTest继承了Thread类,并且包含一个抽象函数:

  public abstract void run()

  正是Thread类中的run()函数的覆盖,其他工具接入时,需要在该函数中编写代码使得自己的工具能够启动。

  AutomatorToolsDriver则是AutomatorToolsTest的管理器,其中包含三个抽象函数:

  public abstract void start()

  public abstract AutomatorToolsTest startTestTask()

  public abstract void dispatchTest()

  作用分别是开始整个测试、启动一个AutomatorToolsTest与开始测试前所需要做的环境和数据的处理。这样做可能在连接多台手机时,获取所有连接着的手机的信息,并且可以做到同时在这些手机上开始测试。

  4.1.3 Mutant Manage模块

  该模块主要与变异生成与变异覆盖率获取相关,由于变异的流程较长,因此将这个模块分离出主流程并进行单独的处理。

  在该模块的流程中,如图4.1中最右侧的流程所示,我们需要先将源代码复制一份备用,接着调用MDroid+工具[12]来生成变异后的代码,这一步由函数

  public void mdpAutoPull.entrance.createMutant()

  方法来完成,但是由于MDroid+只会生成编译后的代码,因此我们还需要通过函数

  public void mdpAutoPull.entrance.createApk()

  来将这些变异过的代码一个个的加入之前复制后的源代码中并进行编译来生成变异apk。接着使用函数

  public void mdpAutoPull.autoExecutio.exec()

  就会自动将这些apk安装到手机上并根据用户输入的测试代码来进行测试了。测试完毕后,会输出日志,最后,根据这些日志,框架会使用函数

  public void mdpAutoPull.autoExecutio.logMerge()

  来将这些日志整合,就可以获知本次测试中有多少变异被覆盖到了。

  4.2 Execution子模块的详细设计与具体实现

  Execution子模块主要包含了五个部分,从图4.1上可以看出,分为插桩、执行、反馈执行数据、计算得分与数据输出。本节会对这五个部分的详细设计与具体实现进行一一讲解。

  4.2.1 插桩

  在本框架中,代码覆盖率是最为主要的得分依据,因此,首先要得到安卓应用的源代码才可以进行测试。但是,虽然现在在学术界有一些可以对安卓应用程序进行反编译以获取源代码的程序,现如今仍然有许多安卓应用程序是加了外壳的,这也就导致工具的不可使用,因此,我们需要用户本身输入源代码给框架进行各种操作。

  综上所述,我们需要为程序进行插桩[13],插桩是指,对代码中注入一些叫做桩的代码,这些代码被运行到了之后,会进行某些操作,在测试完毕后这些代码的状态会被返回到一个新建的文件中,只要分析这个文件,我们就能了解到测试的执行经过了哪些地方,甚至于知道测试的流程是怎样的。在人机对战框架中,我们选择对将安卓程序编译好后生成的.class文件进行插桩,由于.class文件是被源文件生成且可以被覆盖的,这样做可以避免源代码被修改导致的不可恢复的问题。

  那么首先就要来介绍一下Java字节码[14]是什么。

  Java字节码是通过javac(java compile)命令或者其他IDE工具,将Java源文件编译后生成的.class文件中的内容,由于该文件是放在Java自己的JVM(Java虚拟机)中执行的,因此它使得Java实现跨框架运行的功能。

  具体的插桩方式通过Jacoco[15],一款已经存在多年且仍然在不断更新的著名代码覆盖率检测工具来进行完成,该工具使用ASM3.0的接口来进行实现,ASM3.0[16]是一个专门对Java字节码进行操作的API,通过使用该API,我们可以做到对Java字节码的增加、删除、修改等操作。Jacoco会在四种情况下进行插桩,如表4.1所示。

  这四种情况包含了所有字节码中可能出现的情况,而且比每一句字节码都插桩要节省空间,故而选择这种插桩方法。

  在插桩的类文件中,会先设定好一个布尔数组,在P处放置与该数组对应的代码,如果P处被执行到了,那么该布尔数组的对应位置则会被设定为true。当测试指定完毕后,只要检测该布尔数组,则可以知道对应处的执行情况。

  该方法的具体实现,需要人工手动完成。首先要将Jacoco引入到待测应用的项目中。Jacoco官方提供了一份生成覆盖率数据的代码,其中包含方法:

  该方法会扫描当前状态下,类中之前插入的布尔数组的信息,来判断有多少桩已经被覆盖了,并生成一个.ec文件,其中包含了这些信息。该文件是二进制文件,在查阅了Jacoco官方文档后,本文将这些内容按一定规范输出,可以看到如图4.3所示的内容:

  而接下来的一步是需要手动完成插桩工作的原因。由于jacocoTestReport方法需要被触发才能执行,因此我们必须将该方法加入到应用源代码的Acticity相关的类的代码中。即当用户触发Activity中的某个方法时,Jacoco才能生成这样一份覆盖率信息。但是单纯的将方法放入Activity中的Ondestry()方法,会因为某些未知的原因导致该函数并不会被执行到。因此目前的方法是自动化的将jacocoTestReport方法加入到每一个Activity文件中的每一个函数的开头。

  这样做其实是有弊端的,如果每一个操作都会进行数据的生成,会导致交互速度变慢,生成大量无用文件等问题。因此这是一个仍需解决的问题。

  但是以目前的方法来看,将jacocoTestReport方法加入到每一个函数的开头确实是有效的。只是因为弊端较大,因此没有将该过程的自动化加入到框架中。

  4.2.2 执行测试

  由图4.1可知,人机对战框架的执行可以分为人工代码执行和自动化测试工具执行两部分。

  为了保证框架使用的简易型,我们已经将测试代码的执行简化为输入一句代码即可执行的一键化流程。

  对于人工编写的测试代码,由于Java的自动化编译与执行需求项目的引入包的固定,因此我们要求待测代码的项目格式与我们的要求相同,同时,使用Appium作为自动化测试的框架,因此用户的电脑上必须将Appium环境配置完毕,准备完全后,用户只需要提供测试代码的位置、待测Apk的源码位置、待测Apk的位置等相关信息输入框架,框架就会自动执行本次测试并给出结果。

  对于自动化工具而言,首先需要接入我们提供的接口,该接口允许代码编写者执行一个脚本来启动相应的工具,所有的参数只需要在脚本中调整完毕即可。接着,使用者只需要提供自己想选择的,且框架中已接入的工具的名称和待测Apk源码位置、待测Apk的位置等信息,框架就会自动执行本次测试并给出结果。

  执行的具体实现主要由类ManualTest与类AutomatorToolsDriver来完成,后者属于Interface模块,因此暂时不作介绍。而前者主要分为三个部分,执行前置操作、执行与执行后收尾处理。

  上述代码为启动Appium服务的一部分代码节选,人工脚本测试中,框架首先需要自动启动Appium来保证测试可以执行,上述代码会启动Appium服务,并将Appium输出的日志放置在用户输入的输出文件夹中。

  上述代码是开启了手机的Logcat获取,在测试完成后,还需要将其关闭以结束获取。

  接着,框架会对用户提供的测试代码进行编译并运行,过程就和正常的人工Appium测试类似,只是我们将这一整个过程都自动化了。

  在执行完毕后,框架执行过程会进入下一个阶段。

  4.2.3 反馈数据

  无论是执行人工脚本进行测试,还是使用自动化工具进行测试,测试结束后能够获得的信息,格式都是统一的。

  首先是通过插桩获得的布尔数组的信息,根据每个类文件中的布尔数组的内容,我们可以知道这个类文件中,有多少部分被覆盖了,又有多少部分没有被覆盖。进而得到整个项目的源代码的代码覆盖率、分支覆盖率、类覆盖率等等详细的覆盖率信息。

  其次,框架会记录下整个测试过程中手机的日志信息,通过对这些日志信息进行筛选,我们可以分析出整个测试过程中,脚本或者自动化工具触发了多少种类的异常。由于测试的目的就是为了发现错误,所以触发的异常越多我们认为这次测试就是越好的。同时,整个日志和筛选过后的出发的异常种类和每个异常被触发的次数也会被我们保留下来。

  最后,有些工具自身也会记录下它们运行过程中生成的自身的日志,比如Monkey在执行时会在控制台输出当前正在进行的操作信息,这些信息也是可以被记录下来的,具体就要看接口中的实现方式是什么了,如果使用者希望的话,可以对框架进行修改,将这些信息也加入评分中。

  这些数据都会被存放在使用者在一开始输入的存放输出信息的文件夹位置中,以用来进行下一步的数据分析。

  每一种信息的获取的详细实现,都是相互分离的,对于覆盖率信息的获取,我们使用了Jacoco工具官方提供的接口,可以直接将产生的.ec文件转换成HTML文件与XML文件。该过程的具体实现分为两步:

  第一步是读取ec文件中的信息到结构体中。

  该方法会将coverage.exec这个文件中的二进制数据根据相应规则读入到execFileLoader中,该结构体里就会保存本次测试的覆盖率信息,并且可以详细到每一句代码的覆盖上。

  接着,我们会执行createReport方法来生成HTML与XML报告。

  这里利用到了ASM3.0中的visitor类,根据覆盖率信息和编译后的源码的插桩信息进行比对,最后生成出源码的覆盖文档。

  最后,我们使用Jsoup包对生成的HTML格式的文件进行读取,就可以自动化的获取到本次测试的各项覆盖率信息了。

  接着是对于异常触发率的获取,我们首先在上一步中得到了手机在测试过程中的日志信息,接着,我们会在日志信息中扫描与异常相关的行,将它们处理成统一格式并存放到ExceptionLog中,最后,我们只需要对ExceptionLog中的异常进行归并即可知道这次这次测试中触发了多少种异常,每一种异常又被触发了多少次了。

  4.2.4 分析数据并计算得分

  当数据都被记录至文件上并存放到确定的位置后,框架就可以对这些文件进行分析并计算得分了。

  具体的评分标准,我们会在第五章中进行介绍,使用了目前比较主流的测试评判依据,覆盖率作为标准,将测试结果进行量化来完成计算,最终得到一个可视化的数字成绩。

  4.2.5 输出得分与详细报告

  得分和所有的详细信息都会被存放在用户要求的输出位置中,使得如果使用者想要进行更加详细的分析的话,也有据可循。

  框架输出的所有文件有:测试得分、HTML格式与XML格式的详细应用源代码覆盖信息、详细的手机日志、触发的异常种类与出发次数信息、以及使用的工具或框架本身会输出的日志。

  4.3 Interface模块的的详细设计与具体实现

  Interface模块的主要内容是提供给自动化工具的接口,接口分为两个,Driver和Test,后者用来建立一个自动化工具的测试,而前者用于生成并管理多个Test以便于多线程执行。

  用户在输入各项信息后,Driver类会自动安装应用到手机上,并分析apk来获取应用的appPackge等信息,接着根据连着用户电脑的手机数量生成相应的Test类来自动化的执行测试工具。

  以Monkey[18]为例,MonkeyDriver会先调用dispatchTest函数来检测一共有多少台手机被连入了电脑中,并为每一个手机都开启一个测试流程:

  接着,在MonkeyTest中,Monkey就会根据实现配置好的文件来进行测试了。

  同时,MonkeyDriver中,会在测试进行的同时不断的轮询来观测测试是否完全结束:

  原则上是等待三个小时,无论是否测试还在执行,都会被算作结束,当然这个数字是可以被更改的。当所有测试都结束后,日志信息也会自动生成在输出文件夹中,同时也会自动从手机上删除待测应用。至于数据的处理,,就是Execution模块中涉及的部分了,已经在4.2中进行了详细的阐述。

  4.4 Mutant Manager模块的详细设计与具体实现

  变异管理模块主要管理变异测试的一整套流程,将变异测试单独分为一节讲述,原因是这一模块会产生大量的文件,如果直接集成到整个流程中,这一过程会消耗大量的时间,导致运行实现过长等问题。

  安卓变异测试仍然和java代码变异测试类似,先生成几十甚至几百个有一处代码进行了变异的应用,接着使用相同的代码对在每一个变异应用下执行,来观察中途是否出现了错误。在保证测试代码原本就能成功执行完毕的情况下,如果执行变异应用出现了错误,则说明测试代码的健壮性很好,杀死了该变异,反之,则是没有覆盖到这个变异。

  当全部的变异应用都被执行完毕后,就可以观察变异被杀死的百分比,给出变异得分。当然,安卓变异与普通的java变异也仍有不同,由于安卓代码分为安卓特性代码(如manifest文件等)和java代码两部分,因此他们的变异也应当分开论述。

  在我们的框架中,我们使用了名为MDroid+的变异生成工具,他可以自动分析安卓因应用源码,并且对于安卓特性代码和java代码两部分都可以生成变异代码。举例而言,比如我们限定了只生成Wrong Activity这一分类的变异,这种变异是对AndroidManifest.xml文件中的activity信息进行修改,来生成不同的应用。那么MDroid+就会对源代码扫描,然后修改每一个activity的信息,并对每一份都生成一份新的变异代码。如图4.4与图4.5所示:

  图4.4 变异前的代码

  图4.5 变异后的代码

  同时,由于变异现如今只对人工脚本的考核上起到作用,因此也只会在人工脚本相互之间的比较中起到作用。我们会在后台根据源代码生成数个该应用的变异版本,接着自动的使用这一份脚本对这些变异进行测试,根据执行的成功与否判断每一个变异是否被杀死了。具体方法是,我们会在生成变异应用的同时,也生成一个包含本次变异信息的日志文件,里面描述了每一个变异应用对应的变异的代码位置。当测试执行结束后,我们会为变异执行情况生成一个布尔数组,将其与日志文件比对即可以知道有多少变异被成功的杀死了,以及是哪些变异被成功的杀死了,最后我们会输出一个包含被杀死变异情况的.json格式的文件,详细的文件内容我们会在第五章中进行展示。

  4.5 本章小结

  本章详细介绍了人机对战框架的详细设计与具体实现,将整个框架分为数个模块来进行介绍。由于框架本身的实现流程是线性的,因此介绍的顺序也是根据该线性流程进行的。从对应用程序插桩直至输出结果都进行了详细的说明。

  第五章 框架的评分标准与功能测试

  本章会详细介绍目前框架所使用的评分标准,并通过实验获取数据,来证明该评分标准的可靠性以及框架本身的可靠性。

  5.1 框架评分标准

  在框架进行完测试后,我们会获得大量的测试中记录下的信息,列举这些我们获得的信息,分别有:数种代码覆盖率信息,测试过程中触发的异常的种类与数量以及工具本身的日志这三种信息。

  由于第四种信息想要规范化还需要进行定制,因此暂时不计入得分。

  在检查了测试过程之后,我们发现了两个诱因:

  其一是,当时的考试,只进行了一个小时,也就是说,考生原本编写的代码就并不是完善,并没有完全的测试了应用所有的部分。

  其二是,在执行过程中,大部分代码都没有完美的执行到最后,这是由于应用的内容、UI等发生了改变,而测试代码本身并没有更改导致的。很有可能考生在考试时以固定的字符串匹配了某一个控件,而当几个月过去后,应用的内容已经进行了更新,该处的控件已经更改了字符串内容,那么测试代码就会在此处因为无法找寻到控件而直接中断,后面的部分自然也就不会继续执行了。

  由实验结果可知,人机对战框架的人工脚本测试是可用且可靠的,并且使用十分方便,正如之前所提到的,只需要输入一句指令即可完成全部过程,如图5.1所示:

  5.2.3人工脚本变异测试

  我们选择使用5.2.2中,覆盖率最高的那名同学的代码,运行以MainActivity作为选项生成的变异。利用框架,我们先生成了11个变异版本,接着自动的将该同学的测试代码在这11个版本中各运行了一次。最后,我们将脚本执行的情况输出为一个json文件,成功杀死的变异会在该json文件中出现,本次脚本变异测试的结果如图5.2所示,为json文件的内容:

  这11个变异的生成还是很快的,合计2分钟不到就生成完毕了,但是运行时间比较长,约用时20分钟,可以看出,执行过程中,有5个变异被成功的杀死了,因此本次的变异得分应为5/11≈0.45.

  变异测试虽然耗时较长,但是使用方法也十分简单,同样只需要一句命令即可。

  参数较多,是由于MDroid+本身需要的参数就较多。而这些参数的内容也是随着各机的路径不同而异的,因此必须要人工输入。

  我们在本机上使用的实例命令为:java –jar pvc.jar M /BihuDaily /app/src/main/ /MDroidPlus /mutantCode com.white.bihudaily /mutantApk /testCode BiHuDaily1.apk

  不过,变异测试耗时巨大仍然是一个很大的问题,我们使用的开源知乎应用,仅变异就会生成两百份代码,而一份代码的平均执行时间为3-5分钟,那么光一份代码想要跑完全部的变异就需要十几个小时,这显然是令人不可接受的。

  因此,我们后续需要增强对变异的审查,即减少变异生成的数量,像只是改变应用Main Activity这样使得应用必然无法打开,必然会被杀死的变异,从变异生成中剔除出去,来达到减少运行时间的目的。

  5.2.4 自动化工具测试

  对于自动化工具而言,原本准备在前文中提到的三种不同测试策略的自动化工具中各选出一个接入框架中证明框架接口的可用性,但是,在进行了长时间的搜索后发现,目前的自动化工具,基本都致力于研究基于模型的自动化测试工具,随机的自动工具已经很少有新的再被提出了,而过去被提出的随机策略自动工具,如Dynodroid[20],也已经没有了继续的更新,这就导致了兼容性问题发生的风险大幅度上升。而系统化的工具,虽然有不少学者研究,如EvoDroid[21]、AppTag[22]等,虽然有论文被提出,但是由于涉及到比较高深的算法,作者都没有将工具开源,甚至没有将工具放出,这就导致根本无法将这些工具接入到框架中。

  因此,目前只接入了两款工具于框架中,一款是随机策略工具,而另一款是基于模型策略工具,分别是:Monkey与Appcrawler[23]。

  选取这两个工具作为框架接口可用性证明的主要原因是由于它们是近年被提出的,并且也在持续不断地更新。而后者是由国人开发的,与作者也能联系上,在试用过程中也解决了不少问题。

  这次测试,我们使用了一款名为“简豆”的开源应用作为测试,每款工具在该APP上运行10次,取平均值作为结论。

  Monkey的设置为执行3600次事件,每次间隔1000毫秒,而AppCrawler设置为,3600秒的运行后退出,即都各自每次执行1小时。这个时间与慕测平台过往的考试时间也是相同的。

  在我们事后检查执行日志的时候发现,Monkey是每等待一秒后,执行多个动作而不是一个,因此时间自然减少了。

  而AppCrawler则是在中途的某一处触发了应用的BUG,导致应用直接崩溃,于是停止了测试。而由于AppCrawler是一个基于模型的自动化测试工具,就导致每一次测试都会在这个地方出错。当然了,该工具可以通过配置文件来跳过出错的地方,不过那就是对该工具的进阶操作了,如果我们进行了配置,对Monkey而言会有失偏颇。

  综上来看,Monkey似乎在覆盖率上获得了很高的成绩,看似比人工脚本的覆盖率还要高,但是,人工脚本覆盖的仅仅是我们要求的应用的其中一部分,而Monkey是对整个应用进行测试,因此,谁胜谁负仍然尚未可知。不过,我们的目的在于验证框架的可用性,从现在的结果来看,接入的工具是可以正常的被使用的,就已经达到了我们的目的。

  5.3 本章小结

  本章对人机对战框架的评分标准进行了介绍,简而言之就是针对覆盖率、异常触发率以及变异杀死率三个方面切入,综合计算得分。目前,使用者还只能输入决定这三者的比率,但是在将来的版本中也可以自行输入自己的得分依据。同时,我们进行了对比实验来证明了框架的可靠性,从结果上来看,分数与我们的预期也是相差无几的。

  第六章 总结与展望

  6.1 本文总结

  人机对战框架,据调查所知,是第一款致力于于将安卓UI测试结果统一化的框架。本人一开始编写它的目的在于能够在全国大学生测试大赛的人机对战比赛中使用,在这个过程中,框架的内容越来越多,也在后续加入了变异等比赛专用的测试方法。

  现在,人机对战框架已经可以在配置好的情况下一键执行并给出结果,我们相信它能够很好地辅助移动应用人机对战的进行与各大高校测试课程的开展。

  本文主要讲述了人机对战框架的需求分析与设计,以及它的具体实现与使用方法,在实验数据的支撑下,人机对战框架证明了其可用性与可控性。

  本文的主要贡献如下:

  (1)使用ASM接口、JAVA技术与Appium技术,完成了插桩、评分等人机对战框架的人工脚本自动化测试功能,使得只要用户提供安卓应用、应用源码与人工测试脚本,框架就能一键完成使用该脚本的安卓UI测试并给出详细结果。同时完成了人机对战框架的自动化测试工具接口并接入了两款自动化工具,使得用户只要提供安卓应用、应用源码与配置文件,框架就能一键完成该应用的自动化工具测试。同时,更多的自动化工具也只需要完成接口的接入就能直接被框架使用。

  (2)完成了安卓变异测试的功能,使得用户只需要提供安卓应用源码与人工测试脚本,就可以自动生成变异并使用人工测试脚本进行测试,最后给出变异覆盖率。同时,完成了计算分数的算法,并且,这个算法是可以被更改的,因此适用于未来的变更。

  (3)本文完成的框架,据调查,是首个对安卓测试结果进行规范化评分的框架,相信能够给学术界的其他研究者带来一定的启发。

  6.2 后续工作展望

  然而,人机对战框架仍然有着部分不足,主要问题如下:

  (1)应用插桩需要手动进行,也就是前文所述的覆盖率文件获取部分,我们现在是对源代码进行大量的相同函数调用的插入,这会大大增加运行时间与降低运行效率。因此我们在后期会继续进行研究,找到一个减少无用覆盖率文件生成的方法。

  (2)安卓6.0以上的版本需要修改应用源码。由于安卓6.0以上的版本修改了权限相关的代码,因此我们无法直接通过应用内的权限设置来获取手机的权限,必须要通过点击同意权限获取才能够得到存储权限等我们需要的权限,因此,我们必须要将安卓应用源码中的目标SDK进行降级,来保证安卓6.0的新特性不会影响到测试结果。

  (3)变异时间过长,每一份变异,大约需要2-3分钟的编译时间与4-5分钟的执行时间,而一个正常的安卓应用,它的变异结果可能会有上百份之多。而在测试大赛中,每一名选手都要执行这么多份变异。一次小型考试就会有五六十名学生,那么消耗的时间就无法估量了,这明显是不可接受的。因此我们只能在后续工作中对变异进行研究,减少无用或是不必要的变异生成,减少变异的总量来达到降低时间成本的目的。

  本人希望能够在今后的版本中,对这些不足加以改进,直至实现真正的完全自动化执行。这样一来也可以直接将该框架接入到慕测主平台中直接使用。

移动版:一种安卓人机对战测试的框架及评价方法

本文标签: