原文地址:http://blog.csdn.net/zhubaitian/article/details/39777951

在本人之前的一篇文章<<Appium基于安卓的各种FindElement的控件定位方法实践和建议>>第二章节谈到Appium可以通过使用UIAutomator的方法去定位Android界面上的控件,当时只是一笔带过举了个例子。如该文给自己的承诺,今天特撰写此文以描述UIAutomator各种控件定位的方法,以作为前文的姊妹篇互通有无。

1. 背景

为了和前文达成一致,这次的实践对象同样也是使用SDK自带的NotePad应用,同样是尝试去获得在NotesList那个Activity里的Menu Options上面的那个Add note菜单选项。以下是UIAutomatorViewer对界面的一个截图.

但有一个例外的地方是下文的”通过伪xpath方法定位控件“章节实例需要使用到的是NoteEditor这个activity里面的Menu options,因为需要演示通过子控件获得父控件然后得到兄弟控件的功能,UIAutomatorViewer截图如下。

2. 通过文本信息定位

通过控件的text属性定位控件应该是最常用的一种方法了,毕竟移动应用的屏幕大小有限,存在text重复的可能性并不大,就算真的有重复,可以添加其他定位方法来缩写误差。

2.1 UISelector.text方法

  1. addNote = new UiObject(new UiSelector().text("Add note"));
  2. assertEquals(addNote.getText(),"Add note");

该方法通过直接查找当前界面上所有的控件来比较每个控件的text属性是否如预期值来定位控件,挺好理解的,所以就没有必要细说了。

2.2. UISelector.textContains方法

  1. addNote = new UiObject(new UiSelector().textContains("Add"));
  2. assertEquals(addNote.getText(),"Add note");

此方法跟以上方法类似,但是不需要输入控件的全部text信息。

2.3 UISelector.textStartsWith方法

  1. addNote = new UiObject(new UiSelector().textStartsWith("Add"));
  2. assertEquals(addNote.getText(),"Add note");

顾名思义,通过判断一个控件的text的开始是否和预期的字串相吻合来获得控件,其实个人觉得这个方法存在的必要性不强,因为它的功能完全可以用上面的方法或者下面的正则表达式的方法取代。况且既然你提供了textStartsWith方法,为什么你不提供个textEndWith的方法呢!

2.4 UISelector.textMatches方法

  1. addNote = new UiObject(new UiSelector().textMatches("^Add.*"));
  2. assertEquals(addNote.getText(),"Add note");

这个方法是通过正则表达式的方式来比较控件的text来定位控件,这里有意思的是用户使用的正则表达式是有限制的,请看该方法的官方描述:”Set the search criteria to match the visible text displayed for a widget (for example, the text label to launch an app). The text for the widget must match exactly with the string in your input argument“。第一句我们不用管它,关键是第二句,翻译过来就是”目标控件的text(的所有内容)必须和我们输入的正则表达式完全匹配“。什么意思呢?意思就是你不能像往常的正则表达式那样通过比较text的部分吻合来获得控件。以下面代码为例子:

  1. addNote = new UiObject(new UiSelector().textMatches("^Add"));
  2. assertEquals(addNote.getText(),"Add note");

正常来说这个正则表达式是没有问题的,它的意思就是想要“获取以Add开头的text的控件,至于Add字串口面是什么值,没有必要去管它”。但是按照我们上面的官方描述,这样子是不行的,你必须要把正则表达式补充完整以使得正而表达式和控件的text完全进行匹配,至于你用什么通配符或者字串就完全按照正则表达式的语法了。

注意这个限制在UISlector使用所有的正则表达式相关的方法中都有效哦。

3 通过控件的ClassName定位

通过这种方法定位控件存在的一个问题是很容易发生重复,所以一般都是先用这种方法去narrow down目标控件,然后再去添加其他如text判断等条件进行控件定位。

3.1 UISelector.className方法

  1. addNote = new UiObject(new UiSelector().className("android.widget.TextView").text("Add note"));
  2. assertEquals(addNote.getText(),"Add note");

实例中首先通过ClassName找到所有的TextView控件,然后再在这些TextView控件查找text是”Add note“的控件。

3.2 UISelector.classNameMatches方法

  1. addNote = new UiObject(new UiSelector().classNameMatches(".*TextView$"));
  2. assertEquals(addNote.getText(),"Add note");

通过正则表达式判断className是否和预期的一致,注意正则表达式的限制和章节2.4描述的一致。

4. 通过伪xpath方法定位

UISelector类提供了一些方法根据控件在界面的XML布局中的层级关系来进行定位,但是UIAutomator又没有真正的提供类似Appium的findElementWithXpath相关的方法,所以这里我就称之为伪xpath方法。
这个章节使用到的不再是NotesList那个activity里面的menu options,而是NoteEditor这个activity里面的Menu options,里面不止有一个Menu entry。

4.1 通过UiSelector.fromParent或UiObject.getFromParent方法

这种方法我觉得最有用的情况是测试代码当前在操作的是同一层级的一组控件中的某一个控件,转而又需要操作同一层级的另外一个控件的时候。下面的实例就是通过save控件的父控件找到其同一层级的兄弟控件delete。这里分别列出了通过UiObject.getFromParent方法和UiSelector.fromParent方法的实例,事实上他们的功能是一样的。
UiObject.getFromPatrent方法:
  1. save =  new UiObject(new UiSelector().text("Save"));
  2. assertEquals(save.getText(),"Save");
  3. delete = save.getFromParent(new UiSelector().text("Delete"));
  4. assertEquals(delete.getText(),"Delete");

UiSelector.fromParent方法(这个例子有点迂回笨拙,但为了演示功能就将就着看吧):

 
  1. delete = new UiObject(new UiSelector().text("Save").fromParent(new UiSelector().text("Delete")));
  2. assertEquals(delete.getText(),"Delete");

4.2 通过UiSelector.childSelector或UiObject.getChild方法

这种方法是在已知父控件的时候如何快速的查找该父控件下面的子控件。
UiObject.getChild方法:
  1. UiObject parentView = new UiObject(new UiSelector().className("android.view.View"));
  2. save = parentView.getChild(new UiSelector().text("Save"));
  3. assertEquals(save.getText(),"Save");

UiSelector.childSelector方法:

  1. save = new UiObject(new UiSelector().className("android.view.View").childSelector(new UiSelector().text("Save")));
  2. assertEquals(save.getText(),"Save");

5. 通过控件ID定位

在Android API Level18及其以上的版本增加了一个Android控件的属性ResourceId,所以要注意在使用这种方法之前先确保你的目标测试设备和你的UIAutomoator库jar包使用的都是API Level 18以上的版本。例如我自己使用的就是本地sdk中版本19的库:D:\Develops\AndroidSDK\platforms\android-19\uiautomator.jar

5.1 UiSelector.resourceId方法

  1. addNote = new UiObject(new UiSelector().resourceId("android:id/title"));
  2. assertEquals(addNote.getText(),"Add note");

5.2 UiSelector.resourceIdMatches方法

  1. addNote = new UiObject(new UiSelector().resourceIdMatches(".+id/title"));
  2. assertEquals(addNote.getText(),"Add note");

注意正则表达式的限制和章节2.4描述的一致

6. 通过contentDescription定位

在UiAutomator框架和使用了Uiautomator框架的Appium中,控件的属性contentDescription一直是强调开发人员需要添加进去的,因为
  • 有些控件使用其他办法很难或者根本没有办法定位
  • 最重要的是给每个控件的contentDescription设计个唯一值让我们可以非常快速的定位控件,让我们足够敏捷!
以下的实例并没有真正跑过的,因为Notepad应用上面的控件是没有contentDescription这个属性的,但是如果我们假设Add note这个控件的contentDescription是“AddNoteMenuDesc”的话,代码的相应写法应该就如下了。

6.1 UiSelector.description方法

  1. addNote = new UiObject(new UiSelector().description("AddNoteMenuDesc));
  2. assertEquals(addNote.getText(),"Add note");
  1. </pre><h2>6.2 UiSelector.descriptionContains方法</h2></div><div><pre name="code" class="java">            addNote = new UiObject(new UiSelector().descriptionContains("AddNote"));
  2. assertEquals(addNote.getText(),"Add note");

6.3 UiSelector.descriptionStartWith方法

  1. addNote = new UiObject(new UiSelector().descriptionStartsWith("AddNote"));
  2. assertEquals(addNote.getText(),"Add note");

6.4 UiSelector.descriptionMatches方法

  1. //addNote = new UiObject(new UiSelector().descriptionMatches("^AddNote.*$"));
  2. //assertEquals(addNote.getText(),"Add note");

7.通过其他方法定位

除了以上比较常用的方法外,UIAutomator还支持其他一些方法,比如根据控件属性是否可点击可聚焦可长按等来缩小要定位的控件的范围,具体使用方法不一一列举,可以查看以下测试验证代码。

  1. package majcit.com.UIAutomatorDemo;
  2. import com.android.uiautomator.core.UiDevice;
  3. import com.android.uiautomator.core.UiObject;
  4. import com.android.uiautomator.core.UiObjectNotFoundException;
  5. import com.android.uiautomator.core.UiScrollable;
  6. import com.android.uiautomator.core.UiSelector;
  7. import com.android.uiautomator.testrunner.UiAutomatorTestCase;
  8. import static org.hamcrest.Matchers.*;
  9. import static org.hamcrest.MatcherAssert.assertThat;
  10. public class UISelectorFindElementTest extends UiAutomatorTestCase {
  11. public void testDemo() throws UiObjectNotFoundException {
  12. UiDevice device = getUiDevice();
  13. device.pressHome();
  14. // Start Notepad
  15. UiObject appNotes = new UiObject(new UiSelector().text("Notes"));
  16. appNotes.click();
  17. //Sleep 3 seconds till the app get ready
  18. try {
  19. Thread.sleep(3000);
  20. } catch (InterruptedException e1) {
  21. // TODO Auto-generated catch block
  22. e1.printStackTrace();
  23. }
  24. //Evoke the system menu option
  25. device.pressMenu();
  26. UiObject addNote = new UiObject(new UiSelector().text("Add note"));
  27. assertEquals(addNote.getText(),"Add note");
  28. addNote = new UiObject (new UiSelector().checked(false).clickable(true));
  29. assertEquals(addNote.getText(),"Add note");
  30. addNote = new UiObject(new UiSelector().className("android.widget.TextView").text("Add note"));
  31. assertEquals(addNote.getText(),"Add note");
  32. addNote = new UiObject(new UiSelector().classNameMatches(".*TextView$"));
  33. assertEquals(addNote.getText(),"Add note");
  34. //addNote = new UiObject(new UiSelector().description("AddNoteMenuDesc));
  35. //assertEquals(addNote.getText(),"Add note");
  36. //addNote = new UiObject(new UiSelector().descriptionContains("AddNote"));
  37. //assertEquals(addNote.getText(),"Add note");
  38. //addNote = new UiObject(new UiSelector().descriptionStartsWith("AddNote"));
  39. //assertEquals(addNote.getText(),"Add note");
  40. //addNote = new UiObject(new UiSelector().descriptionMatches("^AddNote.*$"));
  41. //assertEquals(addNote.getText(),"Add note");
  42. addNote = new UiObject(new UiSelector().focusable(true).text("Add note"));
  43. assertEquals(addNote.getText(),"Add note");
  44. addNote = new UiObject(new UiSelector().focused(false).text("Add note"));
  45. assertEquals(addNote.getText(),"Add note");
  46. //TBD
  47. //addNote = new UiObject(new UiSelector().fromParent(selector))
  48. addNote = new UiObject(new UiSelector().index(0).text("Add note"));
  49. assertEquals(addNote.getText(),"Add note");
  50. addNote = new UiObject(new UiSelector().className("android.widget.TextView").enabled(true).instance(0));
  51. assertEquals(addNote.getText(),"Add note");
  52. addNote = new UiObject(new UiSelector().longClickable(false).text("Add note"));
  53. assertEquals(addNote.getText(),"Add note");
  54. addNote = new UiObject(new UiSelector().text("Add note"));
  55. assertEquals(addNote.getText(),"Add note");
  56. addNote = new UiObject(new UiSelector().textContains("Add"));
  57. assertEquals(addNote.getText(),"Add note");
  58. addNote = new UiObject(new UiSelector().textStartsWith("Add"));
  59. assertEquals(addNote.getText(),"Add note");
  60. addNote = new UiObject(new UiSelector().textMatches("Add.*"));
  61. assertEquals(addNote.getText(),"Add note");
  62. addNote = new UiObject(new UiSelector().resourceId("android:id/title"));
  63. assertEquals(addNote.getText(),"Add note");
  64. addNote = new UiObject(new UiSelector().resourceIdMatches(".+id/title"));
  65. assertEquals(addNote.getText(),"Add note");
  66. //Go to the editor activity, need to cancel menu options first
  67. device.pressMenu();
  68. //Find out the new added note entry
  69. UiScrollable noteList = new UiScrollable( new UiSelector().className("android.widget.ListView"));
  70. //UiScrollable noteList = new UiScrollable( new UiSelector().scrollable(true));
  71. UiObject note = null;
  72. if(noteList.exists()) {
  73. note = noteList.getChildByText(new UiSelector().className("android.widget.TextView"), "Note1", true);
  74. //note = noteList.getChildByText(new UiSelector().text("Note1"), "Note1", true);
  75. }
  76. else {
  77. note = new UiObject(new UiSelector().text("Note1"));
  78. }
  79. assertNotNull(note);
  80. //Go to the NoteEditor activity
  81. note.click();
  82. device.pressMenu();
  83. UiObject save = null;
  84. UiObject delete = null;
  85. save =  new UiObject(new UiSelector().text("Save"));
  86. assertEquals(save.getText(),"Save");
  87. delete = save.getFromParent(new UiSelector().text("Delete"));
  88. assertEquals(delete.getText(),"Delete");
  89. delete = new UiObject(new UiSelector().text("Save").fromParent(new UiSelector().text("Delete")));
  90. assertEquals(delete.getText(),"Delete");
  91. save = new UiObject(new UiSelector().className("android.view.View").childSelector(new UiSelector().text("Save")));
  92. assertEquals(save.getText(),"Save");
  93. UiObject parentView = new UiObject(new UiSelector().className("android.view.View"));
  94. save = parentView.getChild(new UiSelector().text("Save"));
  95. assertEquals(save.getText(),"Save");
  96. }
  97. }

【转】UIAutomator定位Android控件的方法实践和建议(Appium姊妹篇)的更多相关文章

  1. UIAutomator定位Android控件的方法实践和建议(Appium姊妹篇)

    在本人之前的一篇文章<<Appium基于安卓的各种FindElement的控件定位方法实践和建议>>第二章节谈到Appium可以通过使用UIAutomator的方法去定位And ...

  2. UIAutomator定位Android控件的方法

    UIAutomator各种控件定位的方法. 1. 背景 使用SDK自带的NotePad应用,尝试去获得在NotesList那个Activity里的Menu Options上面的那个Add note菜单 ...

  3. MonkenRunner通过HierarchyViewer定位控件的方法和建议(Appium/UIAutomator/Robotium姊妹篇)

    1. 背景 在使用MonkeyRunner的时候我们经常会用到Chimchat下面的HierarchyViewer模块来获取目标控件的一些信息来辅助我们测试,但在MonkeyRunner的官网上是没有 ...

  4. Robotium之Android控件定位实践和建议(Appium/UIAutomator姊妹篇)

    本人之前以前撰文描写叙述Appium和UIAutomator框架是怎样定位Android界面上的控件的. UIAutomator定位Android控件的方法实践和建议 Appium基于安卓的各种Fin ...

  5. Robotium之Android控件定位实践和建议

    本人之前曾经撰文描述Appium和UIAutomator框架是如何定位Android界面上的控件的. UIAutomator定位Android控件的方法实践和建议Appium基于安卓的各种FindEl ...

  6. 转载:Robotium之Android控件定位实践和建议(Appium/UIAutomator姊妹篇)

    来源于:http://blog.csdn.net/zhubaitian/article/details/39803857 1. 背景 为保持这个系列的一致性,我们继续用SDK自带的NotePad实例应 ...

  7. xe6 android控件透明度设置方法

    今天才知道xe6 android控件的透明度设置方法:只需设置控件中的Opacity参数,默认为1--不透明 panel1.Opacity:=0.60;

  8. Android 控件架构及View、ViewGroup的测量

    附录:示例代码地址 控件在Android开发的过程中是必不可少的,无论是我们在使用系统控件还是自定义的控件.下面我们将讲解一下Android的控件架构,以及如何实现自定义控件. 1.Android控件 ...

  9. 从Android系统出发,分析Android控件构架

    从Android系统出发,分析Android控件构架 Android中所有的控件追溯到根源,就是View 和ViewGroup,相信这个大家都知道,但是大家也许会不太清楚它们之间的具体关系是什么,在A ...

随机推荐

  1. MongoDB最新版本3.2.9下载地址

    https://downloads.mongodb.com/win32/mongodb-win32-x86_64-enterprise-windows-64-3.2.9.zip?_ga=1.22538 ...

  2. Lua面向对象设计(转)

    首先对于Lua语言,它没有打算被用来进行大型的程序设计,相反,Lua目标定于小型到中型的程序设计,通常是作为大型系统的一部分,所以它只提供了一套精简的元素,很多高级语言的概念都没有.这样Lua就成为了 ...

  3. IBatis.net初步使用

    最近加班比较忙,时间也比较琐碎,蛮久没有写东西了.这次就总结一下自己使用IBatis.net的一些总结吧. IBatis简介 IBatis.net是一款开源的Orm框架,应该算是从java的IBati ...

  4. SQL Server中的TempDB管理——TempDB基本知识(为什么需要版本存储区)

    原文:SQL Server中的TempDB管理--TempDB基本知识(为什么需要版本存储区) 参考资料来自: http://blogs.msdn.com/b/sqlserverstorageengi ...

  5. ubuntu初学成长记录

    在说正事以前,我只想说,我在网上找的很多的命令都已经过时了,并不能用,比如有些人说查看版本信息要用”gcc --version“,然而却是用”gcc -v"......... 1.使用GCC ...

  6. ACdream 1427 Nice Sequence

    主题链接:http://115.28.76.232/problem? pid=1427 Nice Sequence Time Limit: 12000/6000MS (Java/Others)Memo ...

  7. OCP-1Z0-051-标题决心-文章2称号

    2. View the Exhibit to examine the description for the SALES table. Which views can have all DML ope ...

  8. Arcgis for Javascript之featureLayer图和属性互操作性

    说明:主要实现加载FeatureLayer并显示属性表,而要实现联动属性表与地图,首先,看看实施后的效果: 显示效果 如上图所看到的,本文章主要实现了下面几个功能:1.FeatureLayer属性表的 ...

  9. AIX6.1/11.2.0.3在有关数据库SWAP一个BUG

    昨天南京到客户服务数据库的优化调整,其中新上线,经过审查alert.log当日志现在是在过去一段时间内取得,每隔几个小时的时间滞后,班会报似的内容: Thu Aug 21 09:01:26 2014 ...

  10. Advanced Installer 9.8打包实录

    原文 Advanced Installer 9.8打包实录 主要介绍:(1)创建工程,(2)创建快捷方式及其图标(3)卸载设置 创建工程(.net为例): 工程创建完成....接下来进行简单设置 开始 ...