Robotium 史上最简单 Robotium 跨进程操作实践——基于 ADB 框架

qing · 2015年01月10日 · 最后由 蜗牛的坚持 回复于 2016年05月03日 · 6916 次阅读
本帖已被设为精华帖!

最开始发在 csdn 的,为了更多小伙伴看到,特在这里再转一下。http://blog.csdn.net/qingchunjun/article/details/42580937

Robotium 是 Android 平台下一款非常优秀的自动化测试框架,它做 android 平台自动化的优势想必看到这篇文章的人应该都很清楚。但优点归优点,缺点也比较多,最明显的缺点有两个,一是必须要和被测系统签名保持一致,二是不能做任何跨进程的操作。

很多小伙伴知道怎么用 robotium,也知道它有这些限制,但不知道其中的原因。这里简单地说一下,robotium 的这些先天不足的主要原因是由于它本身是基于 instrumentation 机制的,这既有好处也有坏处。好处是通过 instrumentation 注入到被测进程,从而与被测进程运行在同一进程空间,使得它能够非常方便地识别被测应用中的被测对象,并对这些对象进行操作。坏处是既然 robotium 已经跟被测应用” 合体” 了,那么根据 android 的进程隔离机制,它自然也被系统隔离在其他进程之外,无法跨进程操作任何对象,如图(1)所示。

图(1)Android 进程沙箱示意图

其实使用相同的签名对于单应用的测试来说并不是难事,网上有很多应用重签名的方法和工具供大家使用。但不能跨进程操作确实成为了 robotium 最大的软肋,很多第三方的应用测试或多或少都有一些跨进程操作的测试场景,所以不少人因为这个问题而放弃了 robotium,甚是可惜啊。

作为 robotium 的铁杆粉之一 (我最开始接触 android 自动化测试就是用的 robotium),断断续续用了好几年,一直觉得是 android 平台最好用的自动化测试工具之一。对于 robotium 怎样突破进程的限制也做过一些研究,网上比较多的解决方案无外乎有以下几种:

  1. 自己写服务做 server,基于 AIDL 或编写 socket 与 monkeyserver 进行通讯,然后在 robotium 测试脚本里调用接口方法来间接地进行跨进程的操作,这种方式可以参考下这篇文章的例子:http://www.robotium.cn/archives/584aidl 接口来进行操作,比如调用拍照操作,所以其实例子并不是很多,技术上的局限性较大。。文章的作者只是给出了思路,我自己曾经按照这种方法去实现过,发现该方法的优势是比较稳定,缺点就是实现起来确实比较复杂,而且有些操作无法找到现成的系统

  2. 基于广播和 service 服务。这种方法的实现方式可以参考http://www.ltesting.net/ceshi/open/kygncsgj/2013/0507/206229.htmlapi,难度也不算小,所以一般不建议使用这种方式。这篇文章的描述。这种方法我没有亲自去试过,但不难发现这种方法原理稍微简单些,但该方法貌似需要系统签名,而且自己写广播和服务来调用系统

综合上面的这些跨进程的解决方案,其实都感觉不太理想,对于很多初学者来说不是太难实现就是局限性比较大,一直没有找到一个比较理想的解决方案。直到后来在 testerhome 上看到一个技术很牛的兄弟把常用的 adb 命令做了一个很完备的封装 (PS:真的是很全了,至少常用的 adb 命令都在,我甚至之前都没有想过 adb 命令还能做这么多事情),做成了一个独立的测试辅助工具。我就突然联想到为何不能使用 adb 命令来辅助 robotium 进行跨进程操作呢?既然该框架已经封装了基于 adb 的所有操作,而且 adb 又是不受系统限制的,那么基于这个框架理论上进行跨进程操作是没有任何问题的。后来经过试验,确实效果不错,轻量级、操作简单、使用方便,并且轻松跨进程,真是居家测试、屌丝逆袭的必备神器啊。好了,闲话不多说了,接下来我们就来详细地看看究竟是怎么用的。

先来简单地介绍下这个 adb 命令框架的常用接口。这个框架里有三个主要的包,分别介绍如下:

xuxu.autotest 这个包里面主要有两个类,一个是 AdbDevice,封装了功能测试中常用的一些操作,如获取当前 activity 的名称和包名、获取设备分辨率、关闭应用、点击对象等常用功能,非常实用。另一个类是 XuImage,顾名思义,即封装了一些常见的图片操作,如得到指定边界的图片截图、对比图片是否一致、截取图片等。

xuxu.autotest.element 这个包主要用于获取被测对象,主要是封装了一个 Position 对象,用于通过 Class Name、Id、Contentdesc 等属性来获取一个被测对象,其底层是通过 uiautomator 来 dump 当前 ui 的 xml 文件的,可以获取到所有对象节点的 xml 文件。

xuxu.autotest.utils 这个包里面主要提供了对 Date 日期时间的操作,ImageUtil 对图片的操作及正则表达式和 shell 语句的操作的封装,方便大家的使用。总的来说整个框架的接口设计还是非常齐全的,可以实现很多常用功能,具体功能大家可以自己看源码和帮助文档来摸索。

接下来就进入我们本次的主题——跨进程。为了方便大家理解,我会在本文中选择两个非常常见的跨进程操作场景来说明如何跨进程,也就是大家喜闻乐见的相机拍照和打电话来进行演示。

例子 1、跨进程操作之相机拍照。

被测程序非常简单,程序界面截图如下图(2)所示:

图(2)

点击第一个界面的 “拍照片” 按钮后,进入界面 2,点击其中的 “拍张照片” 后启动系统的相机,当用户按下拍照功能键后,系统可以将用户拍下的照片显示在应用中,以备后续浏览或者是上传。由于相机应用和我们所写的被测应用是两个不同的应用,所以这就属于典型的跨进程操作,Robotium 框架本身是无法对这个相机界面进行操作的。

接下来看看关键的测试工程怎么创建的。测试工程的创建方法非常简单,操作步骤如下:

  1. 首先按照常规的 android 测试工程的建法,创建一个常规测试工程。

  2. 在工程中引入 robotium 和 adbForAndroid 的 jar 包。

这样,我们的测试工程就准备好了。

接下来的步骤就是写测试脚本。由于 AdbForAndroid 框架是按照元素的相关属性来查找并定位被测对象的,所以首先要弄清楚我们要操作的跨进程的界面上的对象的信息,这些信息我们可以通过很多现成工具来看,我这里选择 android 自带的 uiautomatorviewer。先在模拟器上手工打开被测程序,进入拍照的相机界面,并用 uiautomatorviewer 查看界面元素信息如下图 (3) 所示:


图(3)

我们可以看到,我们要点击的拍照按钮的属性在图(3)的右下角的列表中已全部展现出来。那么我们该选哪个呢?这个问题取决于 adbForAndroid 框架支持由哪些属性来获取元素。通过查询其帮助文档,我们知道目前这个框架中常用的查找元素的方法如下:
findElementByContentdesc
findElementByClass
findElementByText
findElementById

由于我现在使用的模拟器版本为 4.2.2,所以 uiautomatorviewer 无法显示 id 属性,如果大家用的是 4.3 版本以上,就可以看到元素的 id 属性了。
那么现在这种情况来看,我们最好就选择 Contentdesc 属性来定位对象了。代码很简单,如下:
Element element =position.findElementByContentdesc("Shutter button");

adbDevice.tap(element); //点击拍照按钮

好,接下来点击了拍照按钮之后,拍照功能还会让你选择是确定还是取消操作,如下图(4)所示:

图(4)

那么如法炮制,我们通过查询获知,这个用于确定的 “√” 按钮,它的属性能用于定位的其实只有 class 属性。不过这里要注意的是,此时由于我们用的是 class 属性,大家可以看到界面上 class 属性跟我们要点击的 “√” 这个按钮相同的对象很多,所以我们必须用 findElementsByClass 方法了。这个方法返回的是一个 ArrayList,所以我们可以写如下的代码来获取所有 class 属性为” android.widget.ImageView” 的元素列表。

ArrayList imageViews =position.findElementsByClass("android.widget.ImageView");

好,写完之后,现在问题来了,究竟这个数组里面哪个 index 才是对应的我们要点击的 “√” 按钮呢?经过试验,我发现界面上多个相同元素返回到数组中时,对应元素位置是按照界面上的位置从上到下、从左到右来的,所以我们要点击的这个按钮的 index 应该是 4。所以点击它的代码如下:

adbDevice.tap(imageViews.get(4)); //2 是 x,3 是重拍,4 是√

上面的代码运行完后,界面就会回到我们的被测程序,后面的操作就不用我再多说了,大家看看是不是非常简单?大笑

例子 2、跨进程操作之打电话

有了第一个例子的基础,其实第二个例子就很好实现了。被测程序非常简单,如下图(5)所示:

图(5)

点击 “拨打该号码” 后,系统自动进入拨号界面,所以也是典型的跨进程测试场景。

实现方法还是跟例 1 中一样,先还是用 uiautomatorviewer 查看界面中的对象信息,再使用对应的方法来操作对象即可。而且在这个例子中,我还给大家演示了另外一种情况,即有些跨进程操作不但要进行操作,还要取得一些对象属性来进行验证,这也是基本可以的。这里我直接给出我的测试代码 (稍微封装了一下):

public boolean CallUtil(String callNumber){

Element element;

boolean result;

//验证是否拨打了正确的号码

if(callNumber.length() == 11){//正常号码需要转变为 x xxx-xxx-xxxx 的格式

StringformatCallNumber = callNumber.substring(0, 1) + " " +callNumber.substring(1, 4) + "-" + callNumber.substring(4,7) +"-" + callNumber.substring(7, callNumber.length());

element= position.findElementByText(formatCallNumber);

try{

Thread.sleep(2000); //线程休眠 2 秒

}catch (InterruptedException e) {

e.printStackTrace();

}

}else{

element= position.findElementByText(callNumber);//除正常号码外,其他格式的号码不进行格式转换

}

if(element != null){

result= true;

}else{

result= false;

}

element= position.findElementByContentdesc("End"); //挂断电话

if(element != null) adbDevice.tap(element);

return result;

}

好了,整个过程非常简单。我相信能用 robotium 的童鞋用起来应该都没有任何问题,所有代码都是非常易用易懂的。

相信通过前面的实例,大家可以发现说这是 “史上最简单” 的 Robotium 跨进程操作解决方案我估计没人会反对,这并不是夸大事实、博人眼球,确实很简单,功能也很强大。其他也不用我多说了,最后我再来对该框架总结一下下吧:

优点:

  1. 确实非常全,基本封装了 adb 的所有常用命令,它本身就是一个 adb 命令使用大全了。

  2. 框架接口设计清晰易懂,简单明了,封装成 jar 包,用起来也很方便。另外希望大家都能看看它的源码并了解它的实现细节,作者封装得还是很不错的,很值得我们学习,而不仅仅是简单地使用它。

  3. 不需要 root 权限。其实这点还是很重要的,有些公司的测试机是没有 root 权限,用 adb 就很方便。对于不想放弃 robotium,又确实有跨进程操作需求的童鞋来说,真的是非常实用的一个框架。

目前已知的不足:

  1. 手机的版本必须是 4.1 以上,即至少必须支持 uiautomator 的手机才行,因为该框架本身底层是依赖于 uiautomator 来 dump 出对象布局 xml 文件,最终获取对象的坐标进行操作的,所以你的手机版本本身并不支持 uiautomator 的话,就没法实现 dump 操作。

  2. 有些对象是 uiautomator 也无法识别和操作的,那当然这个框架也无能为力了。比如屏幕最上方的通知消息栏的对象,所有工具都没法显示和识别,这个肯定没办法了,另外还比如输入文字时的弹出键盘上的按钮对象等,也无法识别,大家可以试试。

以后想要尝试的改进:

  1. 对于手机版本的支持问题,个人觉得有个曲线救国的方法可以解决。即我们可以稍微修改下源码,加入判断手机版本的代码,判断当前手机版本如果高于 4.1,就直接通过 uiautomator 来 dump,如果低于 4.1,则读取 PC 上事先导出到指定位置的 xml 文件。这样的话,如果你使用的手机不是 4.1 以上的版本,只需要事先把被测应用用 4.1 以上版本的手机通过 uiautomator 先 dump 到 PC 的一个指定路径上就行了,这样效果应该是一样的,只要最终能得到对象坐标就 OK。

  2. 再加一些比较实用的功能。比如现在可以根据 text 来查找对象,但实际上很多时候我们可能是得到对象了,但想通过这个对象去获取对象的其他属性,所以建议增加类似 getXXXXByElement(Element e) 这样的方法,有空试一下,呵呵。

以上只是我的一些个人想法,如果大家还有一些什么别的建议,也欢迎大家都提出来,一起来完善这个很实用的框架。

最后,再次感谢 AdbForAndroid 框架的作者: xuxu。

框架源码 github 地址:https://github.com/gb112211/Adb-For-Test
本文中相关样例工程源码 (含被测程序和测试程序) 下载

共收到 8 条回复 时间 点赞

超赞. 这个方向的确很有价值. 思路很好.

qing #2 · 2015年01月10日 Author

#1 楼 @seveniruby 谢谢,大家一起努力,打造更强的测试工具,呵呵。

跨进程操作一般思路就是 android 利用 uiautomator, 还有就是利用坐标,再者就是 mock 一下绕过去。

思路不错,但有个疑问
既然用到了 uiautomator 为和不直接在 Robotium Case 中获取 UiAutomation 进行跨进程操作?
To get an instance of UiAutomation, call Instrumentation.getUiAutomation()

qing #5 · 2015年02月06日 Author

#4 楼 @fresh 之所以用这个方式,是考虑到这个框架本身已经对这些操作封装得比较好了,对于使用者来说,很有可能他没有用过 uiautomator,不熟悉对 uiautomator 的相关操作。这个方法唯一的好处就是方便,简单,没有什么复杂的东西在里面,仅此而已。如果要单说跨进程,确实倒是有不少的方法和手段可以达到。

试了一下,貌似 没成功。。。

我试了也是没成功,一直提示手机没权限,明明都 root 了。所以个人觉的需要 root 手机 还是一件比较麻烦的事,因为很多测试机是不 root 的

xuxu Robotium 处理跨进程的问题 中提及了此贴 02月04日 13:24
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册