UIAutomator定位Android控件的方法实践和建议

2024-08-26 05:08

本文主要是介绍UIAutomator定位Android控件的方法实践和建议,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 背景

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

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

2. 通过文本信息定位

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

2.1 UISelector.text方法

addNote = new UiObject(new UiSelector().text("Add note"));	assertEquals(addNote.getText(),"Add note");
该方法通过直接查找当前界面上所有的控件来比较每个控件的text属性是否如预期值来定位控件,挺好理解的,所以就没有必要细说了。

2.2. UISelector.textContains方法

addNote = new UiObject(new UiSelector().textContains("Add"));	assertEquals(addNote.getText(),"Add note");
此方法跟以上方法类似,但是不需要输入控件的全部text信息。

2.3 UISelector.textStartsWith方法

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

2.4 UISelector.textMatches方法

addNote = new UiObject(new UiSelector().textMatches("^Add.*"));	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的部分吻合来获得控件  。以下面代码为例子:
addNote = new UiObject(new UiSelector().textMatches("^Add"));	assertEquals(addNote.getText(),"Add note");
正常来说这个正则表达式是没有问题的,它的意思就是想要“获取以Add开头的text的控件,至于Add字串口面是什么值,没有必要去管它”。但是按照我们上面的官方描述,这样子是不行的,你必须要把正则表达式补充完整以使得正而表达式和控件的text完全进行匹配,至于你用什么通配符或者字串就完全按照正则表达式的语法了。

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

3 通过控件的ClassName定位

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

3.1 UISelector.className方法

addNote = new UiObject(new UiSelector().className("android.widget.TextView").text("Add note"));	assertEquals(addNote.getText(),"Add note");
实例中首先通过ClassName找到所有的TextView控件,然后再在这些TextView控件查找text是”Add note“的控件。

3.2 UISelector.classNameMatches方法

addNote = new UiObject(new UiSelector().classNameMatches(".*TextView$"));	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方法:

save =new UiObject(new UiSelector().text("Save"));	assertEquals(save.getText(),"Save");	delete = save.getFromParent(new UiSelector().text("Delete"));	assertEquals(delete.getText(),"Delete");
UiSelector.fromParent方法(这个例子有点迂回笨拙,但为了演示功能就将就着看吧):
delete = new UiObject(new UiSelector().text("Save").fromParent(new UiSelector().text("Delete")));	assertEquals(delete.getText(),"Delete");

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

这种方法是在已知父控件的时候如何快速的查找该父控件下面的子控件。

UiObject.getChild方法:

UiObject parentView = new UiObject(new UiSelector().className("android.view.View"));	save = parentView.getChild(new UiSelector().text("Save"));	assertEquals(save.getText(),"Save");
UiSelector.childSelector方法:
save = new UiObject(new UiSelector().className("android.view.View").childSelector(new UiSelector().text("Save")));	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方法

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

5.2 UiSelector.resourceIdMatches方法

addNote = new UiObject(new UiSelector().resourceIdMatches(".+id/title"));	assertEquals(addNote.getText(),"Add note");
注意正则表达式的限制和章节2.4描述的一致

6. 通过contentDescription定位

在UiAutomator框架和使用了Uiautomator框架的Appium中,控件的属性contentDescription一直是强调开发人员需要添加进去的,因为

  • 有些控件使用其他办法很难或者根本没有办法定位
  • 最重要的是给每个控件的contentDescription设计个唯一值让我们可以非常快速的定位控件,让我们足够敏捷!

以下的实例并没有真正跑过的,因为Notepad应用上面的控件是没有contentDescription这个属性的,但是如果我们假设Add note这个控件的contentDescription是“AddNoteMenuDesc”的话,代码的相应写法应该就如下了。

6.1 UiSelector.description方法

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

6.3 UiSelector.descriptionStartWith方法

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

6.4 UiSelector.descriptionMatches方法

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

7.通过其他方法定位

除了以上比较常用的方法外,UIAutomator还支持其他一些方法,比如根据控件属性是否可点击可聚焦可长按等来缩小要定位的控件的范围,具体使用方法不一一列举,可以查看以下测试验证代码。
package majcit.com.UIAutomatorDemo;import com.android.uiautomator.core.UiDevice;import com.android.uiautomator.core.UiObject;import com.android.uiautomator.core.UiObjectNotFoundException;import com.android.uiautomator.core.UiScrollable;import com.android.uiautomator.core.UiSelector;import com.android.uiautomator.testrunner.UiAutomatorTestCase;import static org.hamcrest.Matchers.*;import static org.hamcrest.MatcherAssert.assertThat;public class UISelectorFindElementTest extends UiAutomatorTestCase {		 public void testDemo() throws UiObjectNotFoundException {	UiDevice device = getUiDevice();	device.pressHome();	// Start Notepad	UiObject appNotes = new UiObject(new UiSelector().text("Notes")); 	appNotes.click();	//Sleep 3 seconds till the app get ready	try {	Thread.sleep(3000);	} catch (InterruptedException e1) {	// TODO Auto-generated catch block	e1.printStackTrace();	}		//Evoke the system menu option	device.pressMenu();	UiObject addNote = new UiObject(new UiSelector().text("Add note"));	assertEquals(addNote.getText(),"Add note");		addNote = new UiObject (new UiSelector().checked(false).clickable(true));	assertEquals(addNote.getText(),"Add note");			addNote = new UiObject(new UiSelector().className("android.widget.TextView").text("Add note"));	assertEquals(addNote.getText(),"Add note");		addNote = new UiObject(new UiSelector().classNameMatches(".*TextView$"));	assertEquals(addNote.getText(),"Add note");		//addNote = new UiObject(new UiSelector().description("AddNoteMenuDesc));	//assertEquals(addNote.getText(),"Add note");		//addNote = new UiObject(new UiSelector().descriptionContains("AddNote"));	//assertEquals(addNote.getText(),"Add note");		//addNote = new UiObject(new UiSelector().descriptionStartsWith("AddNote"));	//assertEquals(addNote.getText(),"Add note");		//addNote = new UiObject(new UiSelector().descriptionMatches("^AddNote.*$"));	//assertEquals(addNote.getText(),"Add note");		addNote = new UiObject(new UiSelector().focusable(true).text("Add note"));	assertEquals(addNote.getText(),"Add note");		addNote = new UiObject(new UiSelector().focused(false).text("Add note"));	assertEquals(addNote.getText(),"Add note");			//TBD	//addNote = new UiObject(new UiSelector().fromParent(selector))		addNote = new UiObject(new UiSelector().index(0).text("Add note"));	assertEquals(addNote.getText(),"Add note");		addNote = new UiObject(new UiSelector().className("android.widget.TextView").enabled(true).instance(0));	assertEquals(addNote.getText(),"Add note");		addNote = new UiObject(new UiSelector().longClickable(false).text("Add note"));	assertEquals(addNote.getText(),"Add note");		addNote = new UiObject(new UiSelector().text("Add note"));	assertEquals(addNote.getText(),"Add note");		addNote = new UiObject(new UiSelector().textContains("Add"));	assertEquals(addNote.getText(),"Add note");		addNote = new UiObject(new UiSelector().textStartsWith("Add"));	assertEquals(addNote.getText(),"Add note");			addNote = new UiObject(new UiSelector().textMatches("Add.*"));	assertEquals(addNote.getText(),"Add note");		addNote = new UiObject(new UiSelector().resourceId("android:id/title"));	assertEquals(addNote.getText(),"Add note");		addNote = new UiObject(new UiSelector().resourceIdMatches(".+id/title"));	assertEquals(addNote.getText(),"Add note");		//Go to the editor activity, need to cancel menu options first	device.pressMenu();	//Find out the new added note entry	UiScrollable noteList = new UiScrollable( new UiSelector().className("android.widget.ListView"));	//UiScrollable noteList = new UiScrollable( new UiSelector().scrollable(true)); 	UiObject note = null;	if(noteList.exists()) {		note = noteList.getChildByText(new UiSelector().className("android.widget.TextView"), "Note1", true);		//note = noteList.getChildByText(new UiSelector().text("Note1"), "Note1", true); 	}	else {		note = new UiObject(new UiSelector().text("Note1"));	}	assertNotNull(note);		//Go to the NoteEditor activity	note.click();	device.pressMenu();		UiObject save = null;	UiObject delete = null;		save =new UiObject(new UiSelector().text("Save"));	assertEquals(save.getText(),"Save");	delete = save.getFromParent(new UiSelector().text("Delete"));	assertEquals(delete.getText(),"Delete");		delete = new UiObject(new UiSelector().text("Save").fromParent(new UiSelector().text("Delete")));	assertEquals(delete.getText(),"Delete");		save = new UiObject(new UiSelector().className("android.view.View").childSelector(new UiSelector().text("Save")));	assertEquals(save.getText(),"Save");		UiObject parentView = new UiObject(new UiSelector().className("android.view.View"));	save = parentView.getChild(new UiSelector().text("Save"));	assertEquals(save.getText(),"Save");				}}

这篇关于UIAutomator定位Android控件的方法实践和建议的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1107630

相关文章

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

浅谈主机加固,六种有效的主机加固方法

在数字化时代,数据的价值不言而喻,但随之而来的安全威胁也日益严峻。从勒索病毒到内部泄露,企业的数据安全面临着前所未有的挑战。为了应对这些挑战,一种全新的主机加固解决方案应运而生。 MCK主机加固解决方案,采用先进的安全容器中间件技术,构建起一套内核级的纵深立体防护体系。这一体系突破了传统安全防护的局限,即使在管理员权限被恶意利用的情况下,也能确保服务器的安全稳定运行。 普适主机加固措施:

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

webm怎么转换成mp4?这几种方法超多人在用!

webm怎么转换成mp4?WebM作为一种新兴的视频编码格式,近年来逐渐进入大众视野,其背后承载着诸多优势,但同时也伴随着不容忽视的局限性,首要挑战在于其兼容性边界,尽管WebM已广泛适应于众多网站与软件平台,但在特定应用环境或老旧设备上,其兼容难题依旧凸显,为用户体验带来不便,再者,WebM格式的非普适性也体现在编辑流程上,由于它并非行业内的通用标准,编辑过程中可能会遭遇格式不兼容的障碍,导致操

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

【北交大信息所AI-Max2】使用方法

BJTU信息所集群AI_MAX2使用方法 使用的前提是预约到相应的算力卡,拥有登录权限的账号密码,一般为导师组共用一个。 有浏览器、ssh工具就可以。 1.新建集群Terminal 浏览器登陆10.126.62.75 (如果是1集群把75改成66) 交互式开发 执行器选Terminal 密码随便设一个(需记住) 工作空间:私有数据、全部文件 加速器选GeForce_RTX_2080_Ti