UI Testing 翻译
除了针对 Android 应用程序中的组件(诸如:Activty,Service,content provider)进行单元测试之外,在运行时对其用户接口(UI)的行为进行测试也很重要。UI 测似确保应用程序有正确的的 UI 输出来响应用户一系列的操作,比如一个按键输入或者按工具条,菜单,对话框,图片和其它 UI 操作。
功能测试或者黑盒 UI 测试并不需要测试人员知道应用的内部实现细节,只需知道当用户执行某些操作或者进行某种输入时的输出即可。这种方法有利于分离团队中测试人员和开发人员的职责。
普通的 UI 测试方法就是手工运行测试并验证应用程序的行为是否符合预期。但是这种方法既沉闷又耗时还容易出现误差。更高效更可靠的方法是用测试框架软件进行 UI 自动化测试。自动化测试创建代码来执行测试任务(test case),测试任务覆盖各种使用场景,这样测试框架就可以以一种可重复的方式自动运行这些 test case。
Overview
Android SDK 提供了下面两个工具,来对你的应用程序进行自动化功能测试:
- uiautomatorviewer - 一个用来对应用程序中组件进行扫描和分析的工具。
- uiautomator - 一个 java 库,其中包含了用于创建自定义 UI 功能测试的 API,和一个用来自动运行测试的运行引擎。
使用这两个工具必须安装下面两个版本的 Android 开发工具:
- Android SDK Tools,Revision 21 or higher
- Android SDK Platform, API 16 or higher
Workflow for the the uiautomator testing framework
- 下面是 UI 自动化测试的简化流程:
- 把待测程序安装到设备上,对应用的 UI 组件进行分析,并确保待测应用程序可以被自动化测试框架访问。
- 创建自动化测试来模拟用户和应用之间的一系列交互。
- 编译 test case 并打包为 jar 文件,然后安装到待测应用所在的测试设备上。
- 运行测试并查看测试结果
- 修正测试中发现的 bug 和 不足之处。
Analyzing Your Application’s UI
在开始写 test case 之前,这有助于我们熟悉待测应用的 UI 组件(包括 View 和控制)。对于连接开发电脑上的任何 Android 设备,我们可以用 uiautomatorviewer 工具对其前台 UI 进行截屏。该工具提供一个方便的可视化界面,用于查看其布局层次以及 View 的属性。利用这些信息,我们接下来就可以创建 uiautomator 测试代码了。
对待测应用程序的 UI 组件进行分析:
1.连接 Android 设备和开发电脑。
2.打开命令行窗口并到 <android-sdk>/tools/ 目录下
3.用下面命令运行该工具:
$ uiautomatorviewer
4.点击 uiautomatorviewer 图形界面上的 Device Screenshot 按钮截屏用于分析。
注意:如果你的电脑上连了多个设备,可以通过设置 ANDROID_SERIAL 环境变量来区分它们:
a.用下面命令查看连接设备的 serial numbers:
$ adb devices
b.通过设置 ANDROID_SEIAL 来选择一个设备用于测试:
Windows:
set ANDROID_SERIAL=<device serial number>
UNIX:
export ANDROID_SERIAL=<device serial number>
如果你只连了一个设备就不需要设置该值。
5.查看应用程序总 UI 属性:
- 在左边的面板中移动鼠标,uiautomatorviewer 就能识别这些组件,然后你就可以看到相应 UI 组件的属性。组件的属性显示在右下角的面板中,布局层次信息列于右上角的面板中。
- 点击 Toggle NAF Nodes 按钮查看 uiautomator 测试框架不能访问的 UI 组件,可能这些组件只有一部分属性是可访问的。
Preparing to Test
在开始使用 uiautomator 测试框架之前,需要完成下面的准备工作:
Load the application to a device
如果你正在阅读该文档,很可能待测 Android 应用程序尚未发布。如果你有 apk 文件的拷贝,就可以通过 adb 工具把它安装到测试设备上去,如何用 adb 工具安装 APK 文件,请参考 adb 文档。
Identify the application’s UI components
在写 uiatomator 测试之前,首先要识别待测应用程序的 UI 组件。通常优秀应用的 UI 组件是可见的并且可以和用户交互的。UI 组件应该有可见的 text 标签或者 android:contentDescription 属性,或者两者兼具。
我们可以用 uiautomatorviewer 工具方便地查看待测应用中可见的屏幕对象(UI 组件)。至于如何通过该工具来分析一个应用程序的屏幕信息,请参考 Analyzing Your Application’s UI 部分。关于 Android 提供了那些常用的 UI 组件,请参考 User Interface。
Ensure that the application is accessible
这一步,是因为 uiautomator 工具运行你的 UI 功能测试代码依赖于 Android 框架支持辅助功能的特性。要使用 uiautomator 工具,至少应该满足一下条件:
- 使用 android:contentDescription 属性对 ImageButton, ImangeView, CheckBox 以及其它空间设置标签。
- 对于 EditText 使用 android:hint 属性而不是 content decription 。
- Associate an
android:hint
attribute with any graphical icons used by controls that provide feedback to the user (for example, status or state information). - 确保所有的可操作元素都可以通过方向控制器访问,比如轨迹球或者方向键。
- 通过 uiautomatorviewer 工具来确保 UI 元素可以被测试框架访问。我们也可以用打开诸如 TalkBack 和 Explore by Touch 这写辅助服务来测试,只通过方向控制来测试我们的应用程序。
关于实现、测试可访问性的更多信息,请参考 Making Applications Accessible。
一般情况下,经由 View 和 ViewGroup 类,Android 程序员可以轻易对辅助功能进行支持。但是有些应用使用自定义 view 组件以提供更好的用户体验。而这些自定义组件并没有从标注 的 Android UI 组件那里继承到对辅助功能的支持。如果你的应用程序属于这种情况,请通过实现 AccessibilityNodeProvider 类来确保 Android 的辅助服务可以访问自定义 UI 组件。关于如何使自定义控件支持辅助功能,请参考 Making Applications Accessible。
Configure your development environment
如果你用 Eclipse 做开发,Android SDK 另外提供了工具来帮助你编写 uiautomator test case 并打包 jar 文件。要设置 Eclipse 以使用这些工具,需要创建一个包含 uiautomator 客户端库的工程,该库一般和 Android SDK 库在一起。配置 Eclipse:
- 在 Eclipse 中创建一个 Java 工程并给该工程取一个名字,该名字和你即将创建的测试相关(例如:MyAppNameTests)。后面就是在该工程中创建 test case,具体创建什么样的 test case 取决于什么样的待测应用。
- 在 Project Explorer 视图中,右键点击刚创建的新工程,然后选择 Properties > Java Build Path,接下来:
a. 点击 Add Library > JUit,然后选择 JUnit3。
b. 点击 Add External JARs… 进入 SDK 目录,在 platforms 目录下,选择最新版本的 SDK目录,并选择 uiautomator.jar 和 android.jar 文件。
如果你不使用 Eclipse 做开发环境,请确保你的 Java class path 包括了 <android-sdk>/platforms/<sdk> 目录下的 uiautomator.jar 和 android.jar 文件。
这些准备工作完成后,马上就可以创建 uiautomator 测试了。
Creating uiautomator Tests
要构建一个可以在 uiautomator 框架中运行的测试,首选要创建一个继承自 UiAutomatorTestCase 的类。在 Eclipse 中这个 test case 文件属于工程的 src 目录。然后,会把这个 test case 构建成一个 JAR 文件,最后把这个 JAR 文件复制到测试设备中。这个 JAR 文件并不是一个 APK 文件,并且和待测应用是分离的。
因为 UiAutomatorTestCase 类继承自 junit.framework.TestCase,所以你可以用 JUnit 的 Assert 类来测试待测应用中的 UI 组件,看是否返回期望结果。要学习更多的 JUnit 知识,你可以阅读 junit.org 主页中的文档。
test case 的首要任务是将要访问安装了待测应用程序的设备。先对设备的 Home screen 做测试来练练手也是一个不错的选择。对于 Home Screen(或者待测应用的其它位置),你可以用 uiautomator 提供的 API 来模拟用户的操作对特定的 UI 组件进行测试。至于如何组织一个 uiautomator test case 请看 sample test case 部分。
uiautomator API
uiautomator API 在 <android-sdk>/platforms/ uiautomator.jar 文件中,该 API 中包含以下几个关键类,通过这些类我们可以捕获、操作待测应用中的 UI 组件:
UiDevice
代表了设备的状态。在测试代码中,可以调用 UiDevice 实例中的函数来检测设备的各种属性状态,例如当前的屏幕的方向或者尺寸。也可以通过 UiDevice 实例来执行设备层次的操作,比如强制改变设备的方向,按下 d-pad 物理按键或者 Home 建和菜单按钮。
下面代码就是获取 UiDevice 实例,然后按下 Home 键:
getUiDevice().pressHome();
UiSelector
表示一个在当前显示界面查询、获取特定组件的搜素条件。如果找到多个匹配组件,那么就返回布局层次中的第一个匹配组件,返回的是一个 UIObject 对象。构造一个 UiSelector 对象时,可以组合多个属性使搜索更精确。如果一个也没找到,就会抛出一个 UiAutomatorObjectNotFoundException 异常。也可以用 childSelector() 函数来嵌套多个 UiSelector 实例。例如,下面的示例代码就展示了如何设定一个搜索条件,在当前显示界面中找到第一个 ListView,然后在这个 ListView 中搜索一个 text 属性为 “Apps”的 UI 组件。
UiObject appItem = new UiObject(new UiSelector() .className("android.widget.ListView").instance(1) .childSelector(new UiSelector().text("Apps")));
UiObject
代表一个 UI 组件。创建 一个 UiObject 实例时通过一个 UiSelector 对象来搜索对应的 UI 组件。
下面的示例代码展示了如何创建 UiObject 对象,这两个对象表示应用程序中的一个 Cancel 按钮和一个 OK 按钮。
UiObject cancelButton = new UiObject(new UiSelector().text("Cancel")); UiObject okButton = new UiObject(new UiSelector().text("OK"));
如果需要,你可以在测试程序的其它地方重用已经创建的 UiObject 实例。要注意的是,在测试代码中你每次用一个 UiObject 实例对一个 UI 组件进行点击操作或者查询其属性,uiautomator 测试框架都会在当前显示界面进行一次搜索。
在下面的示例代码中,uiautomator 测试框架会先搜索 text 属性为 “OK”的 UI 组件,如果存在并且是 enable 的,那么测试框架就会模拟用户对该组件进行点击操作。
if(okButton.exists() && okButton.isEnabled()) { okButton.click(); }
也可以限制搜索条件只搜索特定类的组件。例如,搜索 Button 类的组件:
UiObject cancelButton = new UiObject(new UiSelector().text("Cancel") .className("android.widget.Button")); UiObject okButton = new UiObject(new UiSelector().text("OK") .className("android.widget.Button"));
UiCollection
代表组件的集合,例如一个视频专辑或者收件箱。实例化 UiCollection 和实例化 UiObject 类似都要传一个 UiSelector 进去。对于 UiCollection,UiSelector 用于搜索多个子控件的容器(例如:一个包含子控件的 Layout)。例如,下面的代码片段展示了如何创建一个代表一个视频专辑的 UiCollection,该专辑显示在一个 FrameLayout 中:
UiCollection videos = new UiCollection(new UiSelector() .className("android.widget.FrameLayout"));
如果这些视频都显示在一个 LinearLayout 中,你又想获取这些视频的个数:
int count = videos.getChildCount(new UiSelector() .className("android.widget.LinearLayout"));
如果你要从中找出一个 text 属性为 “Cute Baby Laughting”的视频,并模拟用户去点击它:
UiObject video = videos.getChildByText(new UiSelector() .className("android.widget.LinearLayout"), "Cute Baby Laughing"); video.click();
对于这个 UI 对象上你同样可以模拟其它的用户操作。例如,你可以模拟选择一个与某一视频线关联的 checkbox:
UiObject checkBox = video.getChild(new UiSelector() .className("android.widget.Checkbox")); if(!checkBox.isSelected()) checkbox.click();
UiScollable
代表一个可滚动的 UI 组件集合。你可以用 UiScrollable 来模拟 UI 组件的垂直或者水平滚动。当一个 UI 组件显示在屏幕以外,你需要滚动才能看到它,这时候这个技术就很有用。
例如下面的示例代码演示如何模拟向下滚动设置菜单并点击一个 About tablet 的选项:
UiScrollable settingsItem = new UiScrollable(new UiSelector() .className("android.widget.ListView")); UiObject about = settingsItem.getChildByText(new UiSelector() .className("android.widget.LinearLayout"), "About tablet"); about.click()
关于这些 API 的更多信息请参考 uiautomator。
A sample uiautomator test case
下面的示例代码是一个简单的 test case,它模拟用户在一个通用 Android 设备上进行设置应用程序的操作。该 test case 模拟用户执行这个任务通常会做的所有操作,包括打开 Home Screen,打开 All Apps Screen,滚动找到 Settings app 的图标,然后点击这个图标进入设置界面。
package com.uia.example.my;
// Import the uiautomator libraries
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 LaunchSettings extends UiAutomatorTestCase {
public void testDemo() throws UiObjectNotFoundException {
// 模拟点击 HOME 按钮.
getUiDevice().pressHome();
// 现在在 Home Sceen. 接下来我们模拟用户进入 All Apps 屏
// 如果用 uiautomatorviewer 工具对 Home Screen 进行截图分析// 就会发现 All Apps 按钮的 content-description 属性的值是 “Apps”
// 我们可以用这个属性值创建一个 UiSelector 来找到这个按钮。
UiObject allAppsButton = new UiObject(new UiSelector()
.description("Apps"));
// 模拟进入 All Apps 屏
allAppsButton.clickAndWaitForNewWindow();
// 设置按钮在 All Apps 屏的 “Apps tab” 中。我们创建一个 UiSelector 找到
// text 属性是 “Apps” 的 tab ,然后模拟用户点击进入 Apps tab。
UiObject appsTab = new UiObject(new UiSelector()
.text("Apps"));
// 模拟点击进入 Apps tab
appsTab.click();
// 接下来在 Apps tab 中模拟用户滑动屏幕找到设置程序的图标。
// 因为这个 Apps tab 是可以滑动的,所以我们可以用一个 UiScrollable 对象。
UiScrollable appViews = new UiScrollable(new UiSelector()
.scrollable(true));
// 设置为水平滑动模式(模式是垂直滑动)
appViews.setAsHorizontalList();
// 创建一个 UiSelector 找到设置按钮,
// 然后模拟用户点击加载这个程序
UiObject settingsApp = appViews.getChildByText(new UiSelector()
.className(android.widget.TextView.class.getName()),
"Settings");
settingsApp.clickAndWaitForNewWindow();
// 验证它的 package name 是否符合预期。
UiObject settingsValidation = new UiObject(new UiSelector()
.packageName("com.android.settings"));
assertTrue("Unable to detect Settings",
settingsValidation.exists());
}
}
Building and Deploying Your uiautomator Tests
完成测试代码之后,根据下面步骤来 build 并把 JAR 文件部署到测试机上:
- 创建 build 配置文件用来生成 JAR。打开终端然后运行下面命令来生成 build 配置文件:
<android-sdk>/tools/android create uitest-project -n <name> -t 1 -p <path>
<name> 是包含 uiautomator 测试源代码的工程名字,<path> 是项目文件夹的路径。
- 从命令行设置 ANDORID_HOME 变量:
Windows:
set ANDROID_HOME=<path_to_your_sdk>
UNIX:
export ANDROID_HOME=<path_to_your_sdk>
- 进入工程文件夹,build.xml 文件就在这里。然后 build 生成 JAR 文件。
ant build
- 用 adb push 命令把生成的 JAR 文件部署到测试机
下面是一个例子adb push <path_to_output_jar> /data/local/tmp/
adb push ~/dev/workspace/LaunchSettings/bin/LaunchSettings.jar /data/local/tmp/
Running uiautomator Tests
下面的例子是展示如何运行 LaunchSettings.jar 测试代码。该测试位于 com.uia.example.my 包中:
adb shell uiautomator runtest LaunchSettings.jar -c com.uia.example.my.LaunchSettings
关于 uiautomator 的语法、命令、设置的详细信息请参考 uiautomator。
Best Practices
下面是一些用 uiautomator 框架进行 UI 功能测试的最佳实践:
- 同样的 UI 功能,要在尽可能多的设备上进行测试(例如不同屏幕密度的设备)。
- 同时, 也应该测试一些常规的场景,例如,来电话的时候,断网的情况,切换其它应用的情况。