UiAutomator 具备一些优势和缺点 。
优势:
可用于使用不同分辨率的设备显示器上
事件能够链接至 Android UI 控制。 例如,点击带有文本 “Ok” 的按钮,而无需点击坐标位置(x=450,y=550)。
能够复制复杂的用户操作序列
总是执行相同的动作序列,从而能够帮助我们在不同的设备上收集性能标准。
能够不改变任何 Java* 代码而多次运行并在不同的设备上运行
能够使用设备上的硬件按钮
缺点:
无法配合 OpenGL* 和 HTML5 应用使用,因为这些应用没有 Android UI 组件。
编写 JavaScript* 非常耗时
脚本部署
为了对如何使用 UiAutomator 进行介绍,我将展示一个简单的程序。 该程序是标准的 Android 信息应用,能够向任何一个手机号码发送短信。
以下简要介绍了我们实施的操作:
查找并运行应用程序
创建和发送短信
如您所见,这非常简单。
测试准备
为了分析 UI 界面,我们将使用 uiautomatorviewer。
uiautomatorviewer 展示了 Node Detail 中所有 UI 组件的截屏,因此您能够看到不同的属性。 从属性中,您可以找到目标元素。
定制开发环境
如果您使用的是 Eclipse*:
在 Eclipse 中创建一个新的 Java 项目。 我们将该项目称为: SendMessage
在 Project Explorer 中右击您的项目并点击Properties项
在 Properties 中选择 Java Build Path,并添加所需的库:
点击 Add Library> JUnit 并在 JUnit3 中选择添加 JUnit 支持
点击 Add External JARs ...
在 /platforms/directory 中选择最新版的 SDK。 同样在该目录下,选择下列文件: uiautomator.jar 和 android.jar
如果您使用的是其他开发环境,请确保将 android.jar 和 uiautomator.jar 文件添加至项目设置中。
创建脚本
使用 Java 类在以前创建的文件中创建一个项目, 称其为:SendMessage。 该类继承了 UiAutomatorTestCase 类,使用 Ctrl + Shift + o 键(适用于 Eclipse),添加所需的库。
创建三个函数来测试该应用:
搜索并运行该应用
发送短信
退出应用的主功能表
创建一个可帮助我们运行上述所有特性的函数 — 一种主要函数:
public void test() {
// Here will be called for all other functions
}
查找并运行应用的函数
该函数非常简单。 按主页按钮,显示主窗口后,打开功能表并查找该应用的图标。 点击该图标启动应用。
private void findAndRunApp() throws UiObjectNotFoundException {
// Go to main screen
getUiDevice().pressHome();
// Find menu button
UiObject allAppsButton = new UiObject(new UiSelector()
.description("Apps"));
// Click on menu button and wait new window
allAppsButton.clickAndWaitForNewWindow();
// Find App tab
UiObject appsTab = new UiObject(new UiSelector()
.text("Apps"));
// Click on app tab
appsTab.click();
// Find scroll object (menu scroll)
UiScrollable appViews = new UiScrollable(new UiSelector()
.scrollable(true));
// Set the swiping mode to horizontal (the default is vertical)
appViews.setAsHorizontalList();
// Find Messaging application
UiObject settingsApp = appViews.getChildByText(new UiSelector()
.className("android.widget.TextView"), "Messaging");
// Open Messaging application
settingsApp.clickAndWaitForNewWindow();
// Validate that the package name is the expected one
UiObject settingsValidation = new UiObject(new UiSelector()
.packageName("com.android.mms"));
assertTrue("Unable to detect Messaging",
settingsValidation.exists());
}
所有的类名、按钮上的文本等均来自 uiautomatorviewer。
发送短信
该函数可查找并按下编写新应用的按钮,输入接收文本信息的电话号码并按发送按钮。 电话号码和文本可通过函数参数发送:
private void sendMessage(String toNumber, String text) throws UiObjectNotFoundException {
// Find and click New message button
UiObject newMessageButton = new UiObject(new UiSelector()
.className("android.widget.TextView").description("New message"));
newMessageButton.clickAndWaitForNewWindow();
// Find to box and enter the number into it
UiObject toBox = new UiObject(new UiSelector()
.className("android.widget.MultiAutoCompleteTextView").instance(0));
toBox.setText(toNumber);
// Find text box and enter the message into it
UiObject textBox = new UiObject(new UiSelector()
.className("android.widget.EditText").instance(0));
textBox.setText(text);
// Find send button and send message
UiObject sendButton = new UiObject(new UiSelector()
.className("android.widget.ImageButton").description("Send"));
sendButton.click();
}
显示电话号码和文本信息的字段没有任何特殊功能,因为这些字段的任何文本和描述都不可用。 因此,通过按照元素在界面层级中的排列位置在该实例中来使用元素,便可找到它们。
为了添加将参数传递至脚本的功能,我们可以指定想要发送该信息的位置的数量以及文本信息。 该函数测试()可初始化默认设置,如果系统通过命令行向任何参数发送信息,则可使用以下默认设置进行替换:
// Default parameters
String toNumber = "123456";
String text = "Test message";
String toParam = getParams().getString("to");
String textParam = getParams().getString("text");
if (toParam != null) {
// Remove spaces
toNumber = toParam.trim();
}
if (textParam != null) {
text = textParam.trim();
}
因此,我们可以使用小密钥 -e、参数的第一个名称和第二个值通过脚本中的命令行来传递参数。 例如,我的应用可以发送数字来发送 " 777777 »:-e 至 777777
您可能会遇到一些问题。 例如,该应用不理解一些字符而失败。 包含某些字符的文本无法传达,因为无法理解而失败。 比如:空格键、&、、(、) 、"、' 以及一些 Unicode 字符。 当使用这些字符编写字符串(如空间线:blogspaceblog)时,我将更换这些字符。 因此,当脚本启动 UiAutomator 时,我们将使用能够处理我们的输入参数的脚本。 我们添加了函数测试(),通过该测试,我们可以检查是否有选项、解析参数,如果有,可将其更换为实际字符。 以下是一个示例代码,演示了我们之前插入的内容:
if (toParam != null) {
toParam = toParam.replace("blogspaceblog", " ");
toParam = toParam.replace("blogamperblog", "&");
toParam = toParam.replace("bloglessblog", "
toParam = toParam.replace("blogmoreblog", ">");
toParam = toParam.replace("blogopenbktblog", "(");
toParam = toParam.replace("blogclosebktblog", ")");
toParam = toParam.replace("blogonequoteblog", "'");
toParam = toParam.replace("blogtwicequoteblog", """);
toNumber = toParam.trim();
}
if (textParam != null) {
textParam = textParam.replace("blogspaceblog", " ");
textParam = textParam.replace("blogamperblog", "&");
textParam = textParam.replace("bloglessblog", "
textParam = textParam.replace("blogmoreblog", ">");
textParam = textParam.replace("blogopenbktblog", "(");
textParam = textParam.replace("blogclosebktblog", ")");
textParam = textParam.replace("blogonequoteblog", "'");
textParam = textParam.replace("blogtwicequoteblog", """);
text = textParam.trim();
}
退出应用的主功能表
该函数是我们所实施的函数中最简单的一个。 仅需按下回环中的一个按钮,直至其显示该按钮,即可创建一条新信息。
private void exitToMainWindow() {
// Find New message button
UiObject newMessageButton = new UiObject(new UiSelector()
.className("android.widget.TextView").description("New message"));
// Press back button while new message button doesn't exist
while(!newMessageButton.exists()) {
getUiDevice().pressBack();
}
}
以下是我们的源代码:源代码
package blog.send.message;
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;
public class SendMessage extends UiAutomatorTestCase {
public void test() throws UiObjectNotFoundException {
// Default parameters
String toNumber = "123456";
String text = "Test message";
String toParam = getParams().getString("to");
String textParam = getParams().getString("text");
if (toParam != null) {
toParam = toParam.replace("blogspaceblog", " ");
toParam = toParam.replace("blogamperblog", "&");
toParam = toParam.replace("bloglessblog", "
toParam = toParam.replace("blogmoreblog", ">");
toParam = toParam.replace("blogopenbktblog", "(");
toParam = toParam.replace("blogclosebktblog", ")");
toParam = toParam.replace("blogonequoteblog", "'");
toParam = toParam.replace("blogtwicequoteblog", """);
toNumber = toParam.trim();
}
if (textParam != null) {
textParam = textParam.replace("blogspaceblog", " ");
textParam = textParam.replace("blogamperblog", "&");
textParam = textParam.replace("bloglessblog", "
textParam = textParam.replace("blogmoreblog", ">");
textParam = textParam.replace("blogopenbktblog", "(");
textParam = textParam.replace("blogclosebktblog", ")");
textParam = textParam.replace("blogonequoteblog", "'");
textParam = textParam.replace("blogtwicequoteblog", """);
text = textParam.trim();
}
findAndRunApp();
sendMessage(toNumber, text);
exitToMainWindow();
}
// Here will be called for all other functions
private void findAndRunApp() throws UiObjectNotFoundException {
// Go to main screen
getUiDevice().pressHome();
// Find menu button
UiObject allAppsButton = new UiObject(new UiSelector()
.description("Apps"));
// Click on menu button and wait new window
allAppsButton.clickAndWaitForNewWindow();
// Find App tab
UiObject appsTab = new UiObject(new UiSelector()
.text("Apps"));
// Click on app tab
appsTab.click();
// Find scroll object (menu scroll)
UiScrollable appViews = new UiScrollable(new UiSelector()
.scrollable(true));
// Set the swiping mode to horizontal (the default is vertical)
appViews.setAsHorizontalList();
// Find Messaging application
UiObject settingsApp = appViews.getChildByText(new UiSelector()
.className("android.widget.TextView"), "Messaging");
// Open Messaging application
settingsApp.clickAndWaitForNewWindow();
// Validate that the package name is the expected one
UiObject settingsValidation = new UiObject(new UiSelector()
.packageName("com.android.mms"));
assertTrue("Unable to detect Messaging",
settingsValidation.exists());
}
private void sendMessage(String toNumber, String text) throws UiObjectNotFoundException {
// Find and click New message button
UiObject newMessageButton = new UiObject(new UiSelector()
.className("android.widget.TextView").description("New message"));
newMessageButton.clickAndWaitForNewWindow();
// Find to box and enter the number into it
UiObject toBox = new UiObject(new UiSelector()
.className("android.widget.MultiAutoCompleteTextView").instance(0));
toBox.setText(toNumber);
// Find text box and enter the message into it
UiObject textBox = new UiObject(new UiSelector()
.className("android.widget.EditText").instance(0));
textBox.setText(text);
// Find send button and send message
UiObject sendButton = new UiObject(new UiSelector()
.className("android.widget.ImageButton").description("Send"));
sendButton.click();
}
private void exitToMainWindow() {
// Find New message button
UiObject newMessageButton = new UiObject(new UiSelector()
.className("android.widget.TextView").description("New message"));
// Press back button while new message button doesn't exist
while(!newMessageButton.exists()) {
getUiDevice().pressBack();
sleep(500);
}
}
}
如要生成测试组件的配置文件,请从命令行运行以下命令:编译并运行测试 UiAutomator
/tools/android create uitest-project -n -t -p
,其中 是为测试 UiAutomator 而创建的项目的名称(在我们的案例中: SendMessage); 是所选的设备和 Android API Level(您可以获得已安装设备列表、组(/工具/ android 列表目标); 是包含该项目的目录的路径。
您必须导出环境变量 ANDROID_HOME:
Windows*:
set ANDROID_HOME=
UNIX*:
export ANDROID_HOME=
进入包含项目文件的目录 build.xml(在第 1 步中生成),并运行命令:
ant build
使用 adb push 将 JAR 文件复制至该设备:
adb push /data/local/tmp/
在我们的案例中,该命令为:
adb push /bin/SendMessage.jar /data/local/tmp/
运行该脚本:
adb shell uiautomator runtest /data/local/tmp/SendMessage.jar –c blog.send.message.SendMessage
关于作者
Egor Churaev (egor.churaev@intel.com)— 软件实习生
相关文章与资源: