首页> 中国专利> 基于插桩技术的安卓应用软件自动化测试方法

基于插桩技术的安卓应用软件自动化测试方法

摘要

本发明涉及一种基于插桩技术的安卓应用软件自动化测试方法,包括以下步骤:步骤一、反编译被测试安卓应用软件,生成资源文件与smali源文件;步骤二、编写探针代码,在smali源文件中插入探针代码,获得修改后的smali文件;步骤三、对步骤二获得的smali文件进行重新编译,生成用于测试的应用软件,采用该应用软件完成被测试安卓应用软件的测试。与现有技术相比,本发明采用插桩技术编写探针代码并实现探针代码的插入,最后完成安卓应用软件的测试,具有方法简便、测试可靠等优点。

著录项

  • 公开/公告号CN103970659A

    专利类型发明专利

  • 公开/公告日2014-08-06

    原文格式PDF

  • 申请/专利权人 刘玉光;

    申请/专利号CN201410210389.2

  • 发明设计人 刘玉光;

    申请日2014-05-16

  • 分类号G06F11/36(20060101);

  • 代理机构31225 上海科盛知识产权代理有限公司;

  • 代理人赵继明

  • 地址 313113 浙江省湖州市长兴县泗安镇榆树弄20-1号

  • 入库时间 2023-12-17 00:55:30

法律信息

  • 法律状态公告日

    法律状态信息

    法律状态

  • 2022-04-26

    未缴年费专利权终止 IPC(主分类):G06F11/36 专利号:ZL2014102103892 申请日:20140516 授权公告日:20170118

    专利权的终止

  • 2017-01-18

    授权

    授权

  • 2014-09-03

    实质审查的生效 IPC(主分类):G06F11/36 申请日:20140516

    实质审查的生效

  • 2014-08-06

    公开

    公开

说明书

技术领域

本发明涉及一种软件测试方法,尤其是涉及一种基于插桩技术的安卓应用软 件自动化测试方法。

背景技术

一、当前安卓应用自动化测试的难点

当前安卓应用软件的自动化测试一般采用robotium框架编写自动化测试用例。 通过robotium框架,自动化测试用例在执行时,可以获得被测试安卓应用软件的 UI信息,也可以模拟用户进行按键或触屏操作,从而可以对被测试安卓应用软件 的逻辑进行测试。

但是,由于安卓系统安全性的限制,robotium框架在下面的自动化测试场景中 存在问题:

1.测试用例要求对被测试安卓应用发出的Intent进行检查,以确认被测试安卓 应用各模块内部之间的耦合、被测试安卓应用与安卓系统之间的耦合、被测试安卓 应用与其他应用之间的耦合是否正常。

2.对很多与后台服务有关的安卓应用进行自动化测试时,为降低被测试安卓应 用与服务器之间的耦合,使测试的软件逻辑清晰可控,测试用例要求对服务器进行 Mock。

3.对使用了GoogleMap服务的安卓应用进行自动化测试时,测试用例要求对被 测试安卓应用的Google MAP(V2)的相关功能数据进行检查,以确认被测试安卓应 用的Google Map的相关软件逻辑正常。由于Google Map(V2)是作为第三方库的形 式被测试软件集成,而且Google Map(v2)并不支持自动化测试,所以在robotium 框架下无法对Google Map(v2)进行自动化测试。

二、程序插桩技术

程序插桩技术最早是由J.C.Huang教授提出的,它是在保证被测程序原有逻 辑完整性的基础上在程序中插入一些探针(又称为“探测仪”),通过探针的执行并 抛出程序运行的特征数据,通过对这些数据的分析,可以获得程序的控制流和数据 流信息,从而实现测试目的的方法。

三、安卓应用反编译及smali汇编

安卓应用均采用Java开发,运行在android dalvik java虚拟机之上,可以使用 工具将安卓应用进行反编译,得到资源文件和smali语言的源文件,也可以使用工 具将反编译得到资源文件和smali语言的源文件再次编译打包,生成安卓应用。

smali是android dalvikjava虚拟机反编译后的采用的一种类Jasmin语法的汇编 语言,它是Java语言的指令级描述,与Java语言有一一对应关系。其特点是:

(1)文件命名

在每个smali源文件中只能定义一个类,源文件的名称与类的名称相同,源文 件的扩展名为smali,源文件所在的目录为类的包名。例如:类 com.droidbox.sandbox.DroidboxGoogleMapWrapper所对应的源文件为 com\droidbox\sandbox\DroidboxGoogleMapWrapper.smali。

(2)数据类型

smali语言中,有两种主要的类型:基本数据类型和复杂类型。复杂类型又分 为对象类型和数组。

基本数据类型

smali语言中的基本数据类型与Java中的基本数据类型相同,采用一个字母表 示,与Java中类型对应关系如下表所示:

Smali类

型V Z B S C I J F D

对应Java

类型void boolean byte short char int long float double

对象类型

smali语言中的对象类型即为Java中的类,采用的表示形式为: Lpackage/name/ClassName$InnerClassName;

例如:Lcom/droidbox/sandbox/DroidboxGoogleMapWrapper$MapHelper;

它等同于Java语言中的 com.droidbox.sandbox.DroidboxGoogleMapWrappe r.MapHelper。

数组

smali语言中的数组类型即为Java中的数组,采用的表达形式为:[DataType。 ′[′的个数表示数组的维度。例如:′[I′等同于Java语言中的“int[]”,′[[Ljava/lang/String′ 等同于Java语言中的“java.lang.String[][]”。

(3)寄存器

在Dalvik Java虚拟机中,所有的数据(包括基本数据类型的数据、对象、数 组类型的数据)必须保存在寄存器中才可以使用,执行。

在Smali语言中,有两类寄存器用于保存数据。一类是用于保存局部变量的变 量寄存器,采用字母v表示,用数字索引来区分。另一类是函数内用于保存传入参 数的参数寄存器,采用字母p表示,使用数字索引来区分。

(4)函数及传参

在smali语言中,函数采用的表示形式为:

Lpackage/name/ClassName;->Method(ParameterTypeList)RetumType。

例如:Ljava/util/ArrayList;->get(I)Ljava/lang/Object;

在这个例子中,“Ljava/util/ArrayList;”表示函数所属的类为java.util.ArrayList。 ″get″为函数的名称。″I″为参数类型列表,表示参数只有一个,为int类型。 ″Ljava/lang/Object;″表示函数的返回值为java.lang.Object类型。

在smali语言中,当一个函数被调用的时候,采用寄存器传递函数参数,而且 函数的参数被置于寄存器列表的最后N个寄存器中(N为函数参数的个数;通常, 每个寄存器保存一个参数)。相对于静态函数,由于非静态函数存在隐藏的this参 数,所以非静态函数在进行参数传递时实际传送的参数个数为N+1,而且参数列表 中的第一个参数总是该函数所属的对象。

例如,非静态方法LMyObject;->callMe(II)V有2个整型参数,另外还有一个 隐含的LMyObject;类型的this参数,所以此函数被调用时,共使用了3个寄存器 来传递参数,而且第一个寄存器保存的是该函数所属的对象。

(5)指令与伪指令

与常见汇编语言一样,在smali语言中,也存在伪指令和指令。伪指令用于指 示编译工具如何进行编译,主要功能有声明类名、声明父类、声明接口、声明引用 类、声明函数、声明参数、声明变量等。指令则用于指示Dalvik java虚拟机的执 行,主要包括寄存器操作指令,跳转指令,函数调用指令,函数返回指令,类型强 制转换指令、对象创建指令、进程同步区块指令。

Smali语言的函数调用指令的语法格式为:ins{RegList},Method。

Ins表示具体的函数调用指令。在Smali语言中,invoke-virtual, invoke-virtual/range,invoke-direct,invoke-direct/range为非静态函数调用指令; invoke-static,invoke-static/range为静态函数调用指令。

RegList表示函数调用时用于传递函数参数的寄存器列表。寄存器名称之间采 用逗号分隔。

Method表示函数的全称,包括函数所属的类名,函数的名称(在类内的名称), 函数的参数列表,函数的返回类型。

例如:invoke-virtual{v2,v1},Ljava/util/ArrayList;->get(I)Ljava/lang/Object;

在这个例子中,“invoke-virtual”表示在此处进行虚函数调用。“v2,v1”为寄 存器列表,寄存器”v2”保存类型为Ljava/util/ArrayList;的对象;寄存器”v1”保 存类型为I的数据。“Ljava/util/ArrayList;->get(I)Ljava/lang/Object;”表示在此指令 执行时被调用的函数。

在上面的例子中,尽管“Ljava/util/ArrayList;->get(I)Ljava/lang/Object;”只声明 了一个参数,但是按照Smali语言的参数传递规则,在实际传参时,还是需要传递 两个参数。

发明内容

本发明的目的就是为了克服上述现有技术存在的缺陷而提供一种基于插桩技 术的安卓应用软件自动化测试方法。

本发明的目的可以通过以下技术方案来实现:

一种基于插桩技术的安卓应用软件自动化测试方法,包括以下步骤:

步骤一、反编译被测试安卓应用软件,生成资源文件与smali源文件;

步骤二、编写探针代码,在smali源文件中插入探针代码,获得修改后的smali 文件;

步骤三、对步骤二获得的smali文件进行重新编译,生成用于测试的应用软件, 采用该应用软件完成被测试安卓应用软件的测试。

所述的步骤二中,插入探针代码的方法为:修改类继承关系,实现探针代码的 插入,具体如下:

a1)被测试安卓应用软件经反编译后获得基类和目标类,基类是目标类的父类, 在目标类中存在对父类的目标函数的调用;

a2)构造一探针类,该探针类是基类的子类,完全重载了父类的构造函数,探 针类的构造函数均调用父类的具有相同参数类型的构造函数;

a3)探针类重载所述目标函数,重载后的目标函数中包括探针代码和对目标函 数的调用;

a4)修改目标类的父类为探针类,即将探针代码插入目标类中。

所述的步骤二中,插入探针代码的方法为:替换对非静态函数的调用,实现探 针代码的插入,具体如下:

b1)被测试安卓应用软件经反编译后获得一类A,该类A中存在对类B的一 非静态函数的调用,该非静态函数称为目标函数;

b2)构造一探针函数,该探针函数为全局静态函数,包括探针代码与对目标函 数的调用,且探针函数的第一个参数的类型为目标函数所属的类型,后续参数与目 标函数完全相同;

探针函数调用目标函数时,调用的对象为第一个参数,传递给目标函数的参数 为第一个参数的后续参数,同时保持参数的个数与顺序不变;

b3)将目标函数替换为探针函数,实现探针代码的插入。

将目标函数替换为探针函数时,还包括将非静态函数调用指令修改为静态函数 调用指令的步骤。

所述的步骤二中,插入探针代码的方法为:替换静态函数,实现探针代码的插 入,具体如下:

c1)被测试安卓应用软件经反编译后获得一类A,该类A中存在对类B的一 静态函数的调用,该静态函数称为目标函数;

c2)构造一探针函数,该探针函数为全局静态函数,包括探针代码与对目标函 数的调用,且探针函数的所有参数与目标函数完全相同;

探针函数调用目标函数时,传递给目标函数的参数与传入的参数完全一致;

c3)将目标函数替换为探针函数,实现探针代码的插入。

所述的探针函数的名称与目标函数的名称相对应。

与现有技术相比,本发明在smali语言下,采用插桩技术编写探针代码并实现 探针代码的插入,最后完成安卓应用软件的测试,方法简便,测试可靠,解决了现 有技术中在robotium框架下进行测试的缺点。

附图说明

图l为本发明的流程图;

图2为本发明的原理示意图。

具体实施方式

下面结合附图和具体实施例对本发明进行详细说明。本实施例以本发明技术方 案为前提进行实施,给出了详细的实施方式和具体的操作过程,但本发明的保护范 围不限于下述的实施例。

如图1所示,为了能够保证被测安卓应用原有的逻辑完整性以及插桩的可行 性,本发明方法分为以下几步:

一、反编译被测试安卓应用

在对安卓应用进行反编译后会生成资源文件与smali源文件文件。

二、实现探针代码,插入探针代码

根据Smali语言的语法与面向对象编程的特点,通过以下三种方法实现探针代 码的插入。

方法一:修改类继承关系,实现探针代码的插入。

此方法的原理如下:

假设被测试的应用反编译后得到类A和类C。类C是类A的子类,也就是说 类C继承了类A所有的非私有方法。在类C中存在对父类的非私有方法f的调用。 现需要在类C中对父类的非私有方法f的调用时插入探针代码。

首先,根据类的继承属性,在代码中加入类B。类B是类A的子类,它重载 了类A的非私有方法f。在类B的方法f中,存在探针代码和对类A的非私有方法 f的调用。这样,当类B的非私有方法f被调用时,探针代码与类A的非私有方法 f都得到执行。而且,不考虑探针代码的逻辑时,类B的非私有方法f的逻辑与类 A的非私有方法f的逻辑完全一致。

然后,修改类C的继承关系,使类C的继承自类B。这样,类C在调用父类 的非私有方法f时,会首先调用类B的非私有方法f,再由类B调用类A的非私有 方法f。由于类B的非私有方法f的逻辑与类A的非私有方法f的逻辑保持一致, 所以这种修改实现了将类B的非私有函数f中探针代码插入到类C的代码中,而 且没有影响到类C原有的逻辑。

使用此原理的进行探针代码插桩时,作为需要插入探针代码的类被称为目标 类,例如类C;目标类的原始父类被称为基类,例如类A;提供插桩代码的类被称 为探针类,例如类B。它是基类的派生类,也是插桩完毕后目标类的父类;在基类 中,作为插桩目标的函数被称为目标函数,例如类A中的非私有函数f;在探针类 中,用于替代目标函数的函数被称为探针函数,它是通过对基娄中的目标函数重载 而得。

根据此原理,可以根据插桩的目标编写探针代码。在编写探针代码时,需遵循 如下规则:

1.探针类与目标类具有相同的父类。

2.探针类完全重载了父类的构造函数。探针类的构造函数均调用父类的具有相 同参数类型的构造函数。

3.探针类重载目标函数。

4.探针类的目标函数由探针代码与对目标函数的调用组成。

由于Smali语言与Java语言是一一对应的,所以可以使用Java根据此规则编 写探针类与探针函数,然后再使用反编译工具将探针类的代码转换为Smali语言的 源代码。

根据此原理,将探针代码插入到目标类的修改过程如下:

①修改目标类的父类为探针类,即修改目标类的源文件中关于父类的伪指令 “.super”的参数,使用探针类代替基类。

②在目标类的smali文件中查找对目标函数的调用指令。

③修改目标类的smali文件中的对目标函数的调用指令。保持指令及寄存器 列表不变,修改指令调用的函数,使用探针类中对应的函数替代基类的函数。

这个规则的具体替换过程可以参见下面的例子。在下面的例子中,基类是 FragmentActivity,目标类是TabletMainActivity,探针类是 DroidboxFragmentActivity。

探针加入之前的代码:

使用此方法进行插桩后的代码片段:

探针类

插桩后的代码:

通过这个例子,可以看到,尽管修改了类之间的继承关系,加入了新的类和探 针代码,可是程序的逻辑并没有受到影响。

方法二:非静态函数调用的替换,实现探针代码插入。

将类内对非父类的非静态函数调用替换为静态函数调用,以插入探针代码。其 原理如下:

假设被测试的应用反编译后得到类A。在类A中存在一代码片段,在这代码 片段中,存在对类B的对象b的函数f的调用。现需要在上述代码片段中的对对象 b的函数f的调用的地方插入探针代码。

在前面的背景技术中提到,由于在Dalvik虚拟机中,进行函数传参时,对于 非静态函数,参数列表中传递的第一个参数总是对象自身,后面的参数都与函数的 形参一一对应。所以,如果存在静态函数F,其参数类型与实际传入类B的函数f 的参数列表中参数一一对应,则根据smali语法,在保持不修改寄存器列表的情况 下,可以将代码中的对对象b的函数f的调用替换为对静态函数F的调用。而且, 在替换后,静态函数F的第一个参数传入的实际参数为对象b。如果静态函数F的 内容仅为调用对象b的函数f,且传递给对象b的函数f的参数与函数F的第一个 参数的后续参数完全相同。则此种替换不会影响到代码的原有逻辑。如果在静态函 数F中加入探针代码,则此种替换可以实现探针代码的安全插入。

在使用此原理进行插桩时,将要被替换的函数被称为目标函数,例如前面提到 的类B的函数f;将要作为代码桩被插入的静态函数被称为探针函数,例如前面提 到的函数F。

根据此原理,可以根据插桩的目标编写探针代码。在编写探针代码时,需遵循 如下规则:

1.探针函数为全局静态函数。

2.探针函数的名称与目标函数的名称具有对应关系,以便工具处理。

3.探针函数的第一个参数的类型为目标函数所属的类型,后续参数与目标函数 完全相同(包括个数,参数类型)。

4.探针函数由两部分组成:探针代码与对目标函数的调用。

5.探针函数调用目标函数时,调用的对象为第一个参数,传递给目标函数的参 数为第一个参数的后续参数(保持参数的个数与顺序不变)。

由于Smali语言与Java语言是一一对应的,所以可以使用Java根据此规则编 写探针类与探针函数,然后再使用反编译工具将探针类的代码转换为Smali语言的 源代码。

根据此原理,实现探针代码插入的过程如下:

①根据以上规则中的探针函数与目标函数名称的对应关系,编写探针函数。

②在smali文件中查找对目标函数的调用指令。

③将smali文件中对目标函数的调用的指令进行修改。在修改时,将非静态 函数调用指令修改为静态函数调用指令;保持寄存器列表不变;将目标函数替换为 探针函数。

例如,存在如下代码片段,现欲在代码片段的第22行处插入探针代码,则可 以根据此规则进行插桩。

插桩后的代码片段如下:

探针函数

插桩后的代码

方法三:静态函数的替换,,实现探针代码插入。

采用带有探针代码的静态函数替换需要插桩的静态函数。其原理如下:

假设被测试的应用反编译后得到类A。在类A中存在一代码片段。在这代码 片段中,存在对类B的静态函数f的调用。现需要在上述代码片段中的对类B的 静态函数f的调用之处插入探针代码。

若存在一静态函数F,它的所有参数与类B的静态函数f的参数完全相同,包 括类型与顺序。在静态函数F中,存在探针代码与对类B的静态函数f的调用。

如果修改代码中对类B的静态函数f的调用的语句,使之不再调用类B的静 态函数f,而是调用静态函数F。则在修改后,当上述代码片段被执行时,类B的 静态函数f仍然会被调用,而且,在类B的静态函数f被调用的前后,探针代码也 得到执行。

所以,上述替换并没有影响到原有的代码逻辑,是可以用于探针代码的插入。

在使用此原理进行插桩时,将要被替换的静态函数被称为目标函数,例如静态 函数f;将要作为代码桩被插入的静态函数被称为探针函数,例如静态函数F。

根据此原理,可以根据插桩的目标编写探针代码。在编写探针代码时,需遵循 如下规则:

①探针函数为全局静态函数。

②探针函数的名称与目标函数的名称具有对应关系,以便工具处理。

③探针函数的所有参数与目标函数完全相同,包括个数,参数类型。

④探针函数由两部分组成:探针代码与对目标函数的调用。

⑤探针函数调用目标函数时,传递给目标函数的参数与传入的参数完全一 致,包括个数,参数顺序。

由于Smali语言与Java语言是一一对应的,所以可以使用Java根据此规则编 写探针类与探针函数,然后再使用反编译工具将探针类的代码转换为Smali语言的 源代码。

根据此原理,实现探针代码插入的过程如下:

①根据以上规则中的探针函数与目标函数名称的对应关系,将探针函数转变 为对应的目标函数。

②在smali文件中查找对目标函数的调用指令。

③将smali文件中对目标函数的调用的指令进行修改。在修改时,保持静态 函数调用指令不变;保持寄存器列表不变;将目标函数替换为探针函数。

例如,存在如下代码片段,现欲在代码片段的第2行处插入探针代码,则可以 根据此规则进行插桩。

插桩后的代码片段如下:

探针函数

插桩后的代码

三、生成测试安卓应用

将修改好后的smali文件重新编译,打包,生成用于测试的应用软件。

在具体实施时,整个的测试过程分为两个部分,测试前的准备工作与测试中使 用测试代码桩。

测试探针插入

测试前的准备工作包括编写测试代码桩,使用工具将代码桩插入到被测试安卓 应用中生成最终用于测试的应用,整个工作流程如图2所示。

在图2中,DroidboxTool是一个应用程序,它负责:一、对Sandbox.apk进行 反编译,获得DroidboxClient库的smali源码;二、对被测试的安卓应用Target.apk 进行反编译,得到包含资源文件与smali源文件的反编译结果;三、将DroidboxClient 库的smali源码合并到Target.apk的反编译结果中,并根据前面论述的插桩规则修 改将探针全部插入到Target.apk的反编译结果中;四、重新编译打包处理后的 Target.apk的反编译结果,生成最终用于测试的安卓应用。

Sandbox.apk是一个特殊应用,用于包含DroidboxClient库,最终在 DroidboxTool的帮助下,将java语言编写的DroidboxClient库转换为相应的smali 源码。

DroidboxClient库是有Java语言编写,根据前面论述的插桩规则和测试需求编 写的探针类和探针函数。在当前的实施中,DroidboxClient库的功能主要有:一、 对url的重定向的设定;二、对Intent的监听设置,对Intent的监听结果的查询; 三、对GoogleMap对象的访问,包括查询对象的属性,获取当前GoogleMap的弹 出窗口等。

Target.apk是被测试的安卓应用。

使用测试探针

在使用robotium框架对被测试安卓应用进行测试时,由于在robotium框架的 限制,测试用例不能直接使用Droidboxclient中的类及方法。所以需要在robotium 框架中加入DroidboxTest库。

DroidboxTest库利用java的反射技术,将被测试安卓应用中的DroidboxClient 库中类、对象及数据导出,提供给robotium框架下的测试用例使用。这样,测试 用例就可以与被测试应用通信,访问到被测试应用的运行状态,改变被测试应用的 行为。

去获取专利,查看全文>

相似文献

  • 专利
  • 中文文献
  • 外文文献
获取专利

客服邮箱:kefu@zhangqiaokeyan.com

京公网安备:11010802029741号 ICP备案号:京ICP备15016152号-6 六维联合信息科技 (北京) 有限公司©版权所有
  • 客服微信

  • 服务号