跨多个App的UI测试
Posted 张庚
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了跨多个App的UI测试相关的知识,希望对你有一定的参考价值。
本文翻译自:Testing UI for Multiple Apps
水平有限自己感觉很多地方表达的并不到位,但找不到更好的表达方式,如果您觉着有更好的表达方式,帮助我改进!
跨越多个App进行UI测试
通过跨越多个APP之间的交互来测试你的APPUI,让你确认你的APP表现是否正确,比如:用户在你的APP和其他APP之间或者进入系统UI之间进行切换操作时。一个例子比如用户切换至短信APP它允许用户输入一个文本消息,然后切换到android通讯录来选择要发送的目标,然后再返回短信APP来发送短信。
这节课包括了如何使用Android Testing Support Library提供的UI自动化测试框架来编写这些UI测试。UI自动化API可以让你和设备上可视的元素进行交互,忽视是和哪个activity在进行交互。你的测试可以通过方便的使用描述查阅一个UI组件,比如一个组件展示的text 或者它的内容描述。UI自动化测试可以运行在Android 4.3(Api level 18)或者更高的级别的Api上。
UI自动化测试框架基于instrumentation Api并且借助AndroidJUnitRunner测试运行器来工作。
设置 UI Automator
在你开始使用UI Automator 进行UI测试之前,确保你的工程按照开始你的测试中描述的那样配置了你的测试代码目录和工程依赖。
在你Android APP module的build.gradle 文件中,你必须设置UI Automator library 的依赖:
dependencies {
...
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'
}
为了优化你的UI automator测试,你必须首先检查目标APP的UI组件,确保他们都是可以获得的。这些优化建议将会在下面两部分中讲到。
在设备上检查UI
在设计你的测试之前,你应该在设备上检查UI组件是否是可见的。为了保证你的UI Automator测试 可以获取这些组件,检查这些组件是否有可见的text标签、是否配置了android:contentDescription或者两个都有。
Uiautomatorviewer工具提供了方便的视觉界面来检查布局层级和查看UI组件的属性,他们在设备的前台是可见的。借助这些信息你可以使用UI Automator创建更细腻的测试。比如,你可以创建一个UI seletor,它可以匹配一个特殊的视觉属性。
加载一个uiautomatorviewer工具:
- 加载一个目标APP到物理设备上。
- 将该设备连接到你的开发机器上
- 打开一个终端然后定位到
<android-sdk>/tools/
目录下。 - 用命令运行该工具
$ uiautomatorviewer
查看你的应用的UI的特性
- 在 uiautomatorviewer界面,点击 设备截屏 按钮。
- 通过uiautomatorviewrtool悬浮在左面板的快照可以概览UI组件定义。该组件的特性被列在右面板下方,布局层级在右面版的上方。
- 选择性的点击 Toggle NAF 节点按钮,来查看那些不会被UI Automator 获取的UI组件,不过只能获取这些组件的有限信息。
了解Android 提供的通用UI组件,查看User Interface.
确保你的Activity是可以获得的
UI Automator测试框架依靠Android framework可获得的特性来查看独立的UI元素。作为一个开发者,你应该在你的Activity中实现这些最基本的优化来支持UI automator:
- 使用 android:contentDescription属性来给ImageButton 、ImageView、CheckBox加标签和其他的用户界面控制组件。
- 给EditText提供一个android:hint属性来代替内容描述。
- 将android:hint属性和任何的图形icon结合起来给用户提供反馈(比如,状态信息)
- 使用uiautomatorviewer工具类确保UI组件可以被测试框架获取到。你也可以测试应用程序通过打开获取服务像TalkBack 然后通过触摸来浏览,然后尝试着仅在一个方向上使用你的应用。
通常情况下,APP开发者很容易获得View 和 ViewGroup支持,然而,一些APP使用了自定义view来给用户提供更丰富的用户体验。这些自定义的元素将不会获得标准的UI元素提供的支持。如果你的APP使用了自定义view,确保通过实现 AccessibilityNodeProvider类为Android获取服务暴露了自定义绘图UI元素。
如果自定义view元素包含了单个的元素,让他通过实现Api方法来变得可以被获得。如果自定义view包含了元素,他们不是view自己的(比如,一个WevView,确保它实现了AccessibilityNodeProvider类。)对于包裹类的view它继承子一个存在的包裹容器实现(比如,一个listview),实现AccessibilityNodeProvider 不是必须的。
对于更多信息 关于实现和测试的可获得性,参见 Making Applications Accessible。
创建一个UI Automator测试类
你的UI Automator测试类应该按照JUnit 4测试类的风格来写。为了学习更多关于创建JUnit 4测试类和使用JUnit 4进行断言、注解,查看构建Instrumented单元测试.
在你开始定义测试类的地方添加@RunWith(AndroidJUnit4.class)注解。你也应该声明Android Testing Support Library 中提供的 AndroidJUnitRunner 作为你的默认测试运行器。
这个步骤更详细的描述参见: Run UI Automator Tests on a Device or Emulator.
根据下面的编程规则来编写你的UI Automator测试类:
- 获取UiDevice对象来访问你想测试的设备,通过调用getInstance()方法然后传递一个Instrumentation对象参数进入
- 通过调用 findObject()方法,获取一个UIObject对象来访问在设备上展示的UI组件(比如,正在前台的当前view)。
- 通过调用UiObject的方法来模拟一个具体的和UI组件之间的交互。比如,调用performMultiPointerGesture()来模拟复杂的触摸手势,和setText()来编辑一个text字段。你可以按照需要重复调用第2步和第3步的Api来测试更复杂的用户交互它让更多的UI组件参与进来或者一系列的用户动作。
- Check that the UI reflects the expected state or behavior, after these user interactions are performed.检查当这些用户交互执行后,UI是否按照期望的状态或表现给予了回馈。
这些步骤在下面的章节中被大量使用到。
访问UI组件
UiDevice是最基本的访问和操作你的设备状态的方式。在你的测试中,你可以调用UiDevice方法来检查不同的特性,比如当前方向或者展示的尺寸。你的测试可以使用UiDevice对象来执行设备级别的动作,比如对设备进行旋转,按压D-pad硬件按钮,以及按压 Home 和 Menu 按钮。
最好的做法是从设备的Home屏开始测试,在Home屏幕(或者设备上你选择的其他开始的位置),你可以调用UI Automator Api 提供的方法来选择和具体的UI元素进行交互。
下面的代码片段向你展示了如何来获取UiDevice的实例和模拟按压Home键:
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 level 18)或者更高的版本上,因为UI Automator框架需要的android SDK版本最低不能低于18.
使用findObject()方法来获取一个UiObject它代表了和给定选择标准相匹配的一个view。你可以复用在你的APP测试中的其它部分创建的UiObject实例。需要注意的是,在你每次使用UiObject实例来点击一个UI element 或者查询一个属性时,UI Automator 测试框架就会寻找一个相匹配的显示。
The following snippet shows how your test might construct UiObject instances that represent a Cancel button and a OK button in an app.下面的代码片段向你展示了,你的测试如何创建一个UiObject实例,它代表APP中的一个Cancle按钮和一个OK按钮。
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();
}
具体声明一个selector
如果你想访问APP中的一个具体的UI组件,可以使用 UiSelector类。这个类代表了一个对当前显示的UI的具体元素的查询。
如果有多个匹配元素被找到的话,在布局层级中第一个匹配到的元素将被作为一个UiObject对象返回。当构建一个UiSelector的时候,你可以将多个属性串联起来,来重新组织一个查询。如果没有找到相匹配的UI 元素,将会抛出一个 UiAutomatorObjectNotFoundException
你可以将childSelector()方法和UiSelector实例相互嵌套来使用。比如,下面的代码实例向你展示了如何在你的测试里具体声明一个查询,来寻找当前展示的UI里的第一个listview ,然后来寻找这个listview中的有text属性的UI元素。
UiObject appItem = new UiObject(new UiSelector()
.className("android.widget.ListView")
.instance(1)
.childSelector(new UiSelector()
.text("Apps")));
最好的方式,当具体声明一个selector的时候,你应该使用一个Resource ID (如果你已经给一个UI元素声明了的话) 来代替一个text元素或者 content-descriptor。不是所有的元素都有text元素(比如,toolbar 中的icons)。Text selectors是非常脆弱的,如果你修改UI的话很容易导致测试失败。他们可能也不会跨语言,你的text selectors 可能不会匹配翻译后的strings.
指定对象的状态在你的selector标准里是非常有用的。比如,如果你想选择一系列的被选择的元素,那样你可以uncheck他们,调用checked()方法并传递true作为他们的参数。
执行Actions
一旦你的测试已经获得了一个UiObject 对象,你可以调用在UiObject类中的方法来执行那个对象代表的UI组件的用户交互。 你可以具体声明这些动作为:
- click() :点击UI元素可视区域的中心
- dragTo() : 拖拽这个对象至指定的坐标
- setText() : 在清除完该字段的内容后,给该字段设置内容。相反的,clearTextField()方法清除一个可编辑字段中存在的内容。
- swipeUp() :在UiObject对象上执行swipe up 动作,相似的,swipeDown() , swipeLeft() 和 swipeRight()方法执行相匹配的动作。
UI自动化测试框架允许你发送一个Intent或者加载一个Activity 在不使用shell命令的情况下,通过getContext()方法来获取一个Context对象。
下面的代码片段告诉你如何使用Intent来加载一个App进行测试。这种方式是非常有用的当你仅仅对测试计算器App感兴趣时,并且不担心launcher
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);
}
在集合上执行操作
如果你想模拟和一个集合的条目进行交互(比如,在一个音乐album中的歌曲或者在收件箱中的一个邮件列表),可以使用UiCollection类。为了创建一个UiCollection对象,具体声明一个UiSelector它寻找一个UI容器或者一个其它子UI元素的包装器,比如一个布局view它包含子UI元素。
下面的代码片段向你展示了如何在你的测试中构建一个UiCollection来代表一个video album 它被展示在一个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();
在可滚动的view中执行动作
Use the UiScrollable class to simulate vertical or horizontal scrolling across a display. This technique is helpful when a UI element is positioned off-screen and you need to scroll to bring it into view.使用UiScrollable类来模拟水平或者竖直方向上的滚动。这个技巧是非常有用的,当一个UI元素已经滚动出屏幕后,你需要通过滚动来让它进入视野。
下面的代码片段展示了如何来模拟向下滚动设置菜单,然后点击about选项:
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 断言方法来测试你的App中的UI元素是否返回预期的结果。
下面的代码片段展示了你的测试如何找出一个计算器App的button,按照顺序来点击他们,接着验证展示的结果是否正确。
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自动化测试
你可以从Android Studio或者命令行来运行UI 自动化测试。确保在你的工程中声明了 AndroidJUnitRunner作为默认的instrumentation runner 。
按照开始你的测试中描述的步骤运行你的 UI Automator测试。
以上是关于跨多个App的UI测试的主要内容,如果未能解决你的问题,请参考以下文章