Android自动化测试初探-1:捕获Activity上的Element第一部分:前言Android系统下应用程序的测试现在应该还算是个新的领域,网上关于这方面的资料很多都是基于白盒测试的,一般都是基于JUnit框架和AndroidSDK中android.test等命名空间下的内容进行,但是有一个前提,那就是必须要有应用程序的源代码以提供测试接入点,但是这在很多软件公司中是不现实的。很多测试工程师做的工作是完全黑盒,基本接触不到源代码,白盒测试大部分也是由开发自己完成。回顾一下Windows下的黑盒测试自动化,先前使用的是微软提供的基于.netframework的UIAutomation自动化测试框架(要求版本在.netframework3.0以上,即VS.NET2008开发环境),对与擅长C#语言的人来说,使用起来确认比较好用。本人也写了基于UIAutomation的轻量级的自动化框架,将在以后的博文中引出。那在Android操作系统中能不能做类似于UIAutomation的事情呢?不幸的是,Android的权限控制分的非常清楚,不同程序之间的数据访问只能通过Intent,contentprovider类似的功能实现。也就是说你开发的运行在Android中的自动化程序想要捕获当前运行的AUT(ApplicationunderTest)界面上的控件等Element(该术语引自UIAutomation,觉得翻译成元素有点生硬,故不作翻译)基本不可能,你也拿不到当前activeactivity的引用(截止本文发帖为止,个人暂时没有找到办法获得此引用)。无路可走了?模拟器里面不能走,外面能不能走?或许可以。第二部分:捕获Activity上的Element在Android的SDK中自带了一个对自动化测试比较有用的工具:hierarchyviewer(位于SDK的tools目录下)。在模拟器运行的情况下,使用该工具可以将当前的Activity上的Element以对象树的形式展现出来,每个Element所含的属性也能一一尽显。这有点像Windows上运行的UISPY,唯一遗憾的是不支持事件的触发。不过没有关系,可以想办法绕,当务之急是能在自行编写的自动化测试代码里找到Activity上的Element。第一个想到的办法就是看hierarchyviewer源码,不巧,网上搜了一下,没有资源。或许Google的官网上有,但是上不去。看来只能反编译了,找来XJad,暴力之。虽然反编译出来的代码很多地方提示缺少import,但代码基本上是正确的。看了一下,确实也知道了许多。后来在编写代码的过程中,确实也证明了如果想引用hierarchyviewer.jar这个包并调试,还是需要知道里面的一些设置的。创建基于hierarchyviewer.jar这个包的调用,需要将它和另外两个包,ddmlib.jar(在hierarchyviewer.jar同级目录中有)和org-netbeans-api-visual.jar(需要下载并安装netbeans,在其安装目录中有)一并导入到工程项目中,因为hierarchyviewer的实现依附于这两个包。想在代码中获取Activity上的Element需要进行如下几个步骤(如果使用过hierarchyviewer这个工具后会发现,自动化代码所要写的就是该工具上的使用步骤):1.Ensureadbrunning2.Setadblocation(因为hierarchyviewer和模拟器的沟通完全是依靠adb做的,所以设定正确的adb程序的位置至关重要,本人就曾在这个问题上栽了半天多)3.GetActiveDevice(这个等同动作发生在启动hierarchyviewer工具时)4.StartViewServer(等同于工具上StartServer菜单触发事件)5.LoadScene(等同于工具上LoadViewHierarchy菜单触发事件)6.GetRootViewNode(获得对象树的根节点,这个动作在工具上点击LoadViewHierarchy菜单后会自动加载)7.GetSubViewNode(获得想要查找的ViewNode,这个动作在工具上点击LoadViewHierarchy菜单后会自动加载)说明:上述步骤中一些名称实际上就是hierarchyviewer中所提供的可访问方法名称,如startViewServer、loadScene、rootNode等。另外ViewNode实际上hierarchyviewer中的一个类,表示的对象树上的一个Element。现将部分核心代码粘贴如下:1.SetadblocationSystem.setProperty(hierarchyviewer.adb,E:\\\\Android\\android-sdk-windows\\tools);其中”hierarchyviewer.adb”这个key是hierarchyviewer.jar中指定的,后面的value是存放AndroidSDK的路径。这个目录必须是当前运行的模拟器所对应的adb的目录,不能自行使用其他目录下adb,否则会发生adb进程异常退出的错误。2.GetActiveDeviceIDevice[]devices=null;DeviceBridge.terminate();while(null==devices||0==devices.length){DeviceBridge.initDebugBridge();//itmustwaitforsometime,otherwisewillthrowexceptiontry{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}devices=DeviceBridge.getDevices();}returndevices;以上方法返回的是所有当前运行的Device列表3.StartViewServerDeviceBridge.startViewServer(device);4.LoadSceneViewHierarchyLoader.loadScene(device,Window.FOCUSED_WINDOW);5.GetRootViewNodevhs.getRoot();其中vhs是ViewHierarchyScene的实例对象6.GetSubViewNodepublicViewNodefindFirstChildrenElement(IDevicedevice,ViewNodeentryViewNode,StringelementID){ViewNodenode=null;if(0!=entryViewNode.children.size()){for(inti=0;ientryViewNode.children.size();i++){node=entryViewNode.children.get(i);if(node.id==elementID)returnnode;elsecontinue;}}returnnode;}虽然上述步骤所涉及的代码量不多,但是花了一天多的时间才终究研究出来,写下此文希望对正在研究Android自动化测试的同学们有些帮助。到目前为止,Element已经得到了,接下去就是实现怎么去触发这些Element,如clickbutton,entertext等等,尚需再慢慢研究,等有结果再贴出来分享!Android自动化测试初探-2:Hierarchyviewer捕获Element的实现原理AndroidSDKtools下的工具hierarchyviewer可以展现Device上的Element的层次分布和自身属性,其核心函数之一就是LoadScene,研究后发现其实现方法是向Device的4939端口通过socket的方式发送了一个DUMP的命令,Device会自动处理该命令并将所有Screen上的Element层次结构和属性一并发回,实现代码如下:publicstaticvoidlistElement(IDevicedevice){Socketsocket;BufferedReaderin;BufferedWriterout;socket=null;in=null;out=null;do{DeviceBridge.setupDeviceForward(device);}while(4939!=DeviceBridge.getDeviceLocalPort(device));socket=newSocket();try{socket.connect(newInetSocketAddress(127.0.0.1,DeviceBridge.getDeviceLocalPort(device)));out=newBufferedWriter(newOutputStreamWriter(socket.getOutputStream()));in=newBufferedReader(newInputStreamReader(socket.getInputStream()));System.out.println(==DUMP);out.write((newStringBuilder()).append(DUMP-1).toString());out.newLine();out.flush();do{Stringline;if((line=in.readLine())==null||DONE..equalsIgnoreCase(line))break;line=line.trim();System.out.println(line);}while(true);}catch(IOExceptione){e.printStackTrace();}}运行后的结果摘录其中一部分(button5),列举如下。注:当前device中运行的是2.1SDK中自带的Calculator程序:com.android.calculator2.ColorButton@43b8bee8mText=1,5getEllipsize()=4,nullmMinWidth=1,0mMinHeight=1,0mMeasuredWidth=2,79mPaddingBottom=1,0mPaddingLeft=1,0mPaddingRight=1,0mPaddingTop=1,0mMeasuredHeight=2,78mLeft=2,81mPrivateFlags_DRAWING_CACHE_INVALID=3,0x0mPrivateFlags_DRAWN=4,0x20mPrivateFlags=8,16779312mID=9,id/digit5mRight=3,160mScrollX=1,0mScrollY=1,0mTop=1,0mBottom=2,78mUserPaddingBottom=1,0mUserPaddingRight=1,0mViewFlags=9,402669569getBaseline()=2,54getHeight()=2,78layout_gravity=4,NONElayout_weight=3,1.0layout_bottomMargin=1,0layout_leftMargin=1,1layout_rightMargin=1,0layout_topMargin=1,0layout_height=11,FILL_PARENTlayout_width=11,FILL_PARENTgetTag()=4,nullgetVisibility()=7,VISIBLEgetWidth()=2,79hasFocus()=5,falseisClickable()=4,trueisDrawingCacheEnabled()=5,falseisEnabled()=4,trueis