跨多个应用程序的UI测试
涉及跨多个应用程序交互的用户界面(UI)测试使您可以验证您的应用程序在用户流程跨越其他应用程序或进入系统UI时的行为。这种用户流程的一个例子就是消息应用。他可以使用户输入文本信息,然后启动 Android 联系人选择器,以便用户可以选择消息的接受者,然后将控制权返回到原始的应用中让用户去提交这个消息。
这个课程涵盖了如何使用 Android 测试支持库提供的 UI Automator 测试框架编写这样的 UI 测试。UI Automator API 可以使您与设备上的可见元素交互,不管哪个 Activity 处于焦点。你的测试可以使用方便的描述符来查找 UI 组件,例如组件上显示的文本或者他的内容描述。 UI Automator 测试可以在 Android 4.3(API 18)或更高的设备上运行。
UI Automator 测试框架是一个基于 instrumentation 的API 并且与使用 AndroidJUnitRunner 测试器一起使用。
配置 UI Automator
在开始使用 UI Automator 构建您的 UI 测试之前,请确保配置了您的测试代码位置和项目依赖。
在你的Android app module 的 build.gradle 文件中,你必须设置一个 UI Automator 库的依赖引用:
dependencies {
...
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'
}
要想优化您的 UI Automator 测试,您应该首先检查您的目标应用的 UI 组件,并且确保他们是可以被访问的。这些优化技巧将会在接下来的两节中介绍。
检查设备上的 UI
在设计你的测试之前,检查设备上可见的 UI 组件。为了确保您的 UI Automator 测试可以访问这些组件,检查这些组件是否含有可见的文本标签,android:contentDescription 值或者两个都有。
uiautomatorviewer 工具提供了一种便捷的可视化的界面去检查布局结构并查看设备前台的可见的 UI 组件的属性。这个信息可以使你使用 UI Automator 创建更加细致的测试。例如,你可以创建一个匹配特定可见属性的 UI 选择器。
想要启动 uiautomatorviewer 工具:
在物理设备上启动目标应用
在你的开发机器上连接设备
打开终端并进入到 /tools/ 文件夹。
使用下面的命令运行该工具:
$ uiautomatorviewer
要想查看你的应用的 UI 属性:
在 uiautomatorviewer 界面,点击 Device Screenshot 按钮。
将鼠标悬停在左侧面板的快照上查看由 uiautomatorviewer 工具标识的 UI 组件。属性列在右下方的面板中,布局结构在右上方的面板中。
或者,点击 Toggle NAF Nodes 按钮来查看 UI Automator 无法访问的 UI 组件。只有有限的信息可以用于这些组件。
要想了解更多关于 Android 提供的常见 UI 组件,请看 用户界面.
确保你的 Activity 是可以被访问的
UI Automator 测试框架在已经实现了 Android 辅助功能的应用上执行的更好。当你使用 View 类型的 UI 元素或者 SDK 或支持库的中View 的子类时,你不需要实现辅助功能,因为这些类已经为你完成了。
但是,一些应用使用自定义的 UI 元素提供了更丰富的用户体验。这些元素不会提供自动的访问支持。如果你的应用程序包含不是来自 SDK 或者支持库中的 View 的子类的实例。请确保通过完成以下步骤将可访问性功能添加到这些元素:
创建一个继承自 ExploreByTouchHelper 的类。
通过调用 setAccessibilityDelegate() 方法来将你创建的新类的实例与一个确定的自定义 UI 的元素关联。
有关将访问功能添加到自定义视图元素的更多指导,请看 Building Accessible Custom Views. 想要了解更多关于 Android 上的可访问性的最佳实践,请看 Making Apps More Accessible.
创建一个 UI Automator 测试类
你的 UI Automator 测试类应该像 JUnit 4 测试类一样的方式编写。要想了解更多关于创建 JUnit 4 测试类和使用 JUnit 4 断言和注释,请看 创建一个 Instrement 单元测试类。
在你的测试类定义的开始添加 @RunWith(AndroidJUnit4.class) 注解。你也需要将 Andriod 测试支持库提供的 AndroidJUnitRunner 指定为您的默认测试器。这一步在 Run UI Automator Tests on a Device or Emulator 有更详细的介绍。
在你的 UI Automator 测试类中实现下面的编程模型:
通过调用getInstance() 方法并传递一个 Instrumentation 对象作为参数,来获取一个 UiDevice 对象用以访问你想测试的设备。
通过调用 findObject() 方法来获取一个 UiObject 对象以访问设备上显示的 UI 组件。
通过调用 UiObject 方法来模拟一个特定的用户交互以在该 UI 组件上执行,例如调用 performMultiPointerGesture() 来模拟一个多点触摸手势,setText() 来编辑文本字段。你可以根据需要重复调用步骤 2 和步骤 3 的 API,以便测试涉及多个 UI 组件或者用户动作序列的更加复杂的用户交互。
在这些用户交互执行过后,检查 UI 是否呈现出预期的状态或者行为。
以下各节将详细介绍这些步骤。
访问 UI 组件
UiDevice 对象是访问和操作设备状态的主要的方式。在你的测试中,你可以调用 UiDevice 方法去检查各种属性的状态,例如当前的方向或者显示的大小。你的测试可以使用 UiDevice 对象来执行设备级别的动作,例如强制设备进入特定的旋转状态,按下 D-pad 硬件按钮,然后按 Home(主页)和 Menu(菜单)按钮。
从设备的主屏幕开始你的测试是一种很好的做法。从主屏幕(或者你在设备上选择的一些其他的开始位置),你可以调用 UI Automator API 提供的方法来选择特定的 UI 元素并与其交互。
下面的代码片段展示了你的测试应该如何获得一个 UiDevice 实例并且模拟一个主页按钮的点击:
import org.junit.Before;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.Until;
...
@RunWith(AndroidJUnit4.class)
@SdkSuppress(minSdkVersion = 18)
public class ChangeTextBehaviorTest {
private static final String BASIC_SAMPLE_PACKAGE
= "com.example.android.testing.uiautomator.BasicSample";
private static final int LAUNCH_TIMEOUT = 5000;
private static final String STRING_TO_BE_TYPED = "UiAutomator";
private UiDevice mDevice;
@Before
public void startMainActivityFromHomeScreen() {
// Initialize UiDevice instance
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
// Start from the home screen
mDevice.pressHome();
// Wait for launcher
final String launcherPackage = mDevice.getLauncherPackageName();
assertThat(launcherPackage, notNullValue());
mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)),
LAUNCH_TIMEOUT);
// Launch the app
Context context = InstrumentationRegistry.getContext();
final Intent intent = context.getPackageManager()
.getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE);
// Clear out any previous instances
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intent);
// Wait for the app to appear
mDevice.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)),
LAUNCH_TIMEOUT);
}
}
在这个例子中, @SdkSuppress(minSdkVersion = 18) 语句有助于确认测试将仅会在 Android 4.3(API 18)或者更高的设备上运行,正如 UI Automator 框架要求的那样。
使用 findObject() 方法来检索一个 UiObject 对象,它代表了一个匹配给定选择条件的视图。你可以根据需要重复使用你在你的应用测试的别的部分创建的 UiObject 实例。注意,每当测试使用 UiObject 实例去点击一个 UI 组件或者查询一个属性时, UI Automator 测试框架都会在当前显示中搜索匹配项。
下面的代码片段展示了你的测试应该如何构建一个代表应用程序中 “取消” 按钮和 “确定” 按钮的 UiObject 实例。
UiObject cancelButton = mDevice.findObject(new UiSelector()
.text("Cancel"))
.className("android.widget.Button"));
UiObject okButton = mDevice.findObject(new UiSelector()
.text("OK"))
.className("android.widget.Button"));
// Simulate a user-click on the OK button, if found.
if(okButton.exists() && okButton.isEnabled()) {
okButton.click();
}
指定一个选择器
如果你想访问一个应用中指定的 UI 控件,请使用 UiSelector 类。这个类代表了对当前显示 UI 上的指定元素的查询。
如果找到了多个匹配的元素,那么布局结构中的第一个匹配的元素将会作为目标 UiObject 被返回回来。当侯建 UiSelector 时,你可以将多个属性连接在一起来优惠你的搜索。如果没有匹配的 UI 元素被找到,将会抛出 UiAutomatorObjectNotFoundException 异常。
你可以使用 childSelector() 方法来嵌套多个 UiSelector 对象。例如,下面的代码展示了你的测试应该如何查找当前显示的 UI 中第一个 ListView ,然后查找该 ListView 中具有文本属性 Apps 的 UI 元素。
UiObject appItem = new UiObject(new UiSelector()
.className("android.widget.ListView")
.instance(0)
.childSelector(new UiSelector()
.text("Apps")));
最好的做法是,当你指定一个选择器时,你应该使用资源 ID 而不是文本元素或者内容描述。并不是所有的元素都有文本元素,(例如,toolbar 上的图标)。文本选择器是脆弱的,如果 UI 发生细微的变化,则有可能导致测试失败。他们也有可能不能跨越不同的语言,您的文本选择器可能不匹配翻译的字符串。
在你的选择器条件中指定对象状态可能是有用的。例如,当你想选择所有被选中 (check) 的元素以便你可以取消选择 (uncheck) 他们,调用 checked() 方法并将参数设置为 true。
执行操作
一旦你的测试获取了一个 UiObject 对象,你可以调用 UiObject 类中的方法在该对象表示的 UI 组件上执行用户操作。你可以指定如下操作:
click( ):点击 UI 元素可见边界的中心。
dragTo( ):拖动对象至任意的坐标点。
settext( ):在清除字段的内容之后,在可编辑字段设置文本。相反的,clearTextField() 方法将会清除可编辑字段中已存在的文本。
swipeUp( ):对 UiObject 对象执行向上滑动操作。相似的,swipeDown( ) ,swipeLeft( ) ,和 swipeRight( ) 方法执行相关的操作。
UI Automator 测试框架允许您通过 getContext() 方法获得一个 Context 对象来发送一个 Intent 或者启动一个 Activity,而不用通过 shell 命令。
下面的代码片段展示了你的测试应该如何使用一个 Intent 来启动被测试应用。当你仅仅对测试这个计算器应用感兴趣而不关心启动时,这个方法是有效的。
public void setUp() {
...
// Launch a simple calculator app
Context context = getInstrumentation().getContext();
Intent intent = context.getPackageManager()
.getLaunchIntentForPackage(CALC_PACKAGE);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
// Clear out any previous instances
context.startActivity(intent);
mDevice.wait(Until.hasObject(By.pkg(CALC_PACKAGE).depth(0)), TIMEOUT);
}
对集合进行操作
如果你想模拟在一些项目的集合(例如,音乐专辑中的歌曲或者收件箱中的邮件列表)上的用户交互,请使用 UiCollection 类。要想创建一个 UiCollection 对象,请指定一个 UiSelector ,用来搜索 UI 容器或者其他子 UI 元素的包装器,例如包含子 UI 元素的布局视图。
下面的代码片段展示了你的测试应该如何构造一个 UiCollection 来表示 FrameLayout 中显示的视频专辑:
UiCollection videos = new UiCollection(new UiSelector()
.className("android.widget.FrameLayout"));
// Retrieve the number of videos in this collection:
int count = videos.getChildCount(new UiSelector()
.className("android.widget.LinearLayout"));
// Find a specific video and simulate a user-click on it
UiObject video = videos.getChildByText(new UiSelector()
.className("android.widget.LinearLayout"), "Cute Baby Laughing");
video.click();
// Simulate selecting a checkbox that is associated with the video
UiObject checkBox = video.getChild(new UiSelector()
.className("android.widget.Checkbox"));
if(!checkBox.isSelected()) checkbox.click();
对滚动视图执行操作
使用 UiScrollable 类来模拟显示器上的竖直或者横向的滚动。当一个 UI 元素位于屏幕外并且需要滚动以将他显示在视图里面时,此技术就很有用。
下面的代码片段展示了应该如何向下滚动“设置”菜单并且单击“关于平板电脑”选项:
UiScrollable settingsItem = new UiScrollable(new UiSelector()
.className("android.widget.ListView"));
UiObject about = settingsItem.getChildByText(new UiSelector()
.className("android.widget.LinearLayout"), "About tablet");
about.click();
验证结果
InstrumentationTestCase 继承自 TestCase ,因此你可以使用标准的 JUnit Assert 方法来测试应用中的 UI 组件返回的期盼的结果。
下面的代码片段展示了你的测试如何可以定位计算器应用中的一些按钮,并且按顺序点击他们,然后验证显示的是否是正确的结果。
private static final String CALC_PACKAGE = "com.myexample.calc";
public void testTwoPlusThreeEqualsFive() {
// Enter an equation: 2 + 3 = ?
mDevice.findObject(new UiSelector()
.packageName(CALC_PACKAGE).resourceId("two")).click();
mDevice.findObject(new UiSelector()
.packageName(CALC_PACKAGE).resourceId("plus")).click();
mDevice.findObject(new UiSelector()
.packageName(CALC_PACKAGE).resourceId("three")).click();
mDevice.findObject(new UiSelector()
.packageName(CALC_PACKAGE).resourceId("equals")).click();
// Verify the result = 5
UiObject result = mDevice.findObject(By.res(CALC_PACKAGE, "result"));
assertEquals("5", result.getText());
}
在设备或模拟器上运行 UI Automator 测试
你可以从 Android Studio 或者命令行运行 UI Automator 测试。确保将 AndroidJUnitRunner 作为你工程默认的 instrumentation 工具。
要想运行你的 UI Automator 测试,请按照 测试入门 中的描述的运行 instrumented 测试的步骤。