Android UI 测试框架总结

1、简述

1.1 总结:

Google官方推荐的UI测试有两种:单应用界面测试Espresso和多应用界面测试UI Automator (例如与系统应用交互)。两种都是基于JUnit测试框架实现的,通过Juit框架中的AndroidJUnitRunner运行实现,此测试运行程序与 JUnit 3 和 JUnit 4(最高为 JUnit 4.10)测试兼容,都是基于插桩(在实体设备和模拟器上运行)的测试框架。可以混合使用,亦可单独使用,但鉴于两者的差异、APK包容量增加、代码维护的考量,需根据需求慎重选择合适的测试框架方案。

1.2 Espresso 与UI Automator区别:

涵盖单个应用的界面测试(Espresso ):这种类型的测试可验证目标应用在用户执行特定操作或在其 Activity 中输入特定内容时的行为是否符合预期。它可让检查目标应用是否返回正确的界面输出来响应应用 Activity 中的用户交互。单应用界面测试框架可让以编程方式模拟用户操作,并测试复杂的应用内用户交互。

涵盖多个应用的界面测试(UI Automator):这种类型的测试可验证不同用户应用之间交互或用户应用与系统应用之间交互的正确行为。例如,可能想要测试相机应用是否能够与第三方社交媒体应用或默认的 Android 相册应用正确分享图片。支持跨应用交互的界面测试框架可让针对此类场景创建测试。

2、Espresso 测试框架

2.1简介

Espresso 测试框架是基于插桩的 API,可与 AndroidJUnitRunner 测试运行程序一起使用。由 AndroidX Test 提供的 Espresso 测试框架提供了一些 API,用于编写界面测试以模拟单个目标应用内的用户交互。Espresso 测试可以在搭载 Android 2.3.3(API 级别 10)及更高版本的设备上运行。使用 Espresso 的主要好处在于,它可以自动同步测试操作与正在测试的应用的界面。Espresso 会检测主线程何时处于空闲状态,以便可以在适当的时间运行测试命令,从而提高测试的可靠性。此外,借助该功能,不必在测试代码中添加任何计时解决方法,如 Thread.sleep()。

2.2设置 Espresso

在使用 Espresso 构建界面测试之前,请务必设置对 Espresso 库的依赖项引用:

dependencies {

androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'

}

在测试设备上关闭动画 - 如果让系统动画在测试设备上保持开启状态,可能会导致意外结果或导致测试失败。通过以下方式关闭动画:在“设置”中打开“开发者选项”,然后关闭以下所有选项:窗口动画缩放、过渡动画缩放、Animator 时长缩放

2.3创建 Espresso 测试类

如需创建 Espresso 测试,请遵循以下编程模型:

通过调用 onView() 方法或 AdapterView 控件的 onData() 方法,在 Activity 中找到要测试的界面组件(例如,应用中的登录按钮)。

通过调用 ViewInteraction.perform() 或 DataInteraction.perform()方法并传入用户操作(例如,点击登录按钮),模拟要在该界面组件上执行的特定用户交互。如需对同一界面组件上的多项操作进行排序,请在方法参数中使用逗号分隔列表将它们链接起来。

根据需要重复上述步骤,以模拟目标应用中跨多个 Activity 的用户流。

执行这些用户交互后,使用 ViewAssertions 方法检查界面是否反映了预期的状态或行为。

2.4 Espresso 与 ActivityTestRule 一起使用

下文介绍如何创建新的 JUnit 4 型 Espresso 测试,并使用 ActivityTestRule 减少需要编写的样板代码量。通过使用 ActivityTestRule,测试框架会在带有 @Test 注释的每个测试方法运行之前以及带有 @Before 注释的所有方法运行之前启动被测 Activity。该框架将在测试完成并且带有 @After 注释的所有方法都运行后关闭该 Activity。

2.5访问界面组件

必须先指定界面组件或视图,然后 Espresso 才能与被测应用进行交互。Espresso 支持使用 Hamcrest 匹配器指定应用中的视图和适配器。

如需查看视图,请调用 onView() 方法并传入用于指定目标视图的视图匹配器。指定视图匹配器部分对此进行了更详细的说明。onView() 方法将返回一个 ViewInteraction 对象,该对象允许测试与视图进行交互。但是,如果希望在 RecyclerView 布局中查找视图,调用 onView() 方法可能不起作用。在这种情况下,请按照在 AdapterView 中查找视图中的说明进行操作。

注意:onView() 方法不检查指定的视图是否有效。相反,Espresso 根据提供的匹配器仅搜索当前视图层次结构。如果未找到匹配项,该方法会抛出 NoMatchingViewException。

2.6指定视图匹配器

可以使用以下方法指定视图匹配器:

调用 ViewMatchers 类中的方法。不能保证 Android 资源 ID 是唯一的。如果测试尝试匹配由多个视图使用的某个资源 ID,Espresso 会抛出 AmbiguousViewMatcherException。

使用 Matchers 类。可以使用 allOf() 方法组合多个匹配器,例如 containsString() 和 instanceOf()。此方法可让更精细地过滤匹配结果。可以使用 not 关键字过滤与匹配器不对应的视图,如需在测试中使用这些方法,请导入 org.hamcrest.Matchers 软件包。如需详细了解 Hamcrest 匹配,请访问 Hamcrest 网站。

如需提高 Espresso 测试的性能,请指定查找目标视图所需的最少匹配信息。例如,如果某个视图可通过其描述性文本进行唯一标识,无需指定该视图也可从 TextView 实例分配。

2.7在 AdapterView 中查找视图

在 AdapterView 微件中,视图会在运行时由子视图动态填充。如果要测试的目标视图位于 AdapterView(例如 ListView、GridView 或 Spinner)内,则 onView() 方法可能不起作用,因为只能将一部分视图加载到当前视图层次结构中。

应改为调用 onData()方法获取 DataInteraction 对象,以访问目标视图元素。Espresso 负责将目标视图元素加载到当前视图层次结构中。Espresso 还负责滚动到目标元素,并将该元素置于焦点上。

注意:onData() 方法不检查指定的项是否与视图对应。Espresso 仅搜索当前视图层次结构。如果未找到匹配项,该方法会抛出 NoMatchingViewException。

2.8执行操作

调用 ViewInteraction.perform() 或 DataInteraction.perform() 方法,以模拟界面组件上的用户交互。必须将一个或多个 ViewAction 对象作为参数传入。Espresso 将按照给定的顺序依次触发每项操作,并在主线程中执行这些操作。

ViewActions 类提供了用于指定常见操作的辅助程序方法的列表。可以将这些方法用作方便的快捷方式,而不是创建和配置单个 ViewAction 对象。可以指定以下操作:

ViewActions.click():点击视图。

ViewActions.typeText():点击视图并输入指定的字符串。

ViewActions.scrollTo():滚动到视图。目标视图必须是由 ScrollView 派生的子类,并且其 android:visibility 属性的值必须为 VISIBLE。对于扩展 AdapterView 的视图(例如 ListView),onData() 方法将负责为滚动。

ViewActions.pressKey():使用指定的键码执行按键操作。

ViewActions.clearText():清除目标视图中的文本。

如果目标视图位于 ScrollView 内,请先执行 ViewActions.scrollTo() 操作以在屏幕中显示该视图,然后再继续执行其他操作。如果已显示该视图,则 ViewActions.scrollTo() 操作将不起作用。

2.9使用 Espresso Intent 单独测试 Activity

Espresso Intent 支持对应用发出的 intent 进行验证和打桩。使用 Espresso Intent,可以通过以下方式单独测试应用、Activity 或服务:拦截传出 intent,对结果进行打桩,然后将其发送回被测组件。

如需开始使用 Espresso Intent 进行测试,需要将以下代码行添加到应用的 build.gradle 文件中:

dependencies {

androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0'

}

如需测试 intent,需要创建 IntentsTestRule 类(与 ActivityTestRule 类非常相似)的实例。IntentsTestRule 类会在每次测试前初始化 Espresso Intent,终止托管 Activity,并在每次测试后释放 Espresso Intent。

2.10使用 Espresso Web 测试 WebView

使用 Espresso Web,可以测试包含在 Activity 中的 WebView 组件。它使用 WebDriver API 检查和控制 WebView 的行为。

如需开始使用 Espresso Web 进行测试,需要将以下代码行添加到应用的 build.gradle 文件中:

dependencies {

androidTestImplementation 'androidx.test.espresso:espresso-web:3.1.0'

}

在使用 Espresso Web 创建测试的过程中,当实例化 ActivityTestRule 对象以测试 Activity 时,需要在 WebView 上启用 JavaScript。在测试中,可以选择 WebView 中显示的 HTML 元素并模拟用户交互,例如在文本框中输入文本,然后点击某个按钮。完成这些操作后,可以验证网页上的结果是否与预期结果一致。

2.11验证结果

调用 ViewInteraction.check() 或 DataInteraction.check() 方法以断言界面中的视图与某种预期状态匹配。必须将 ViewAssertion 对象作为参数传入。如果断言失败,Espresso 会抛出 AssertionFailedError。

ViewAssertions 类提供了用于指定常见断言的辅助程序方法的列表。

可以使用的断言包括:

doesNotExist:断言当前视图层次结构中没有符合指定条件的视图。

matches:断言当前视图层次结构中存在指定的视图,并且其状态与某个给定的 Hamcrst 匹配器匹配。

selectedDescendentsMatch:断言存在父视图的指定子视图,并且其状态与某个给定的 Hamcrst 匹配器匹配。

2.12在设备或模拟器上运行 Espresso 测试

可以通过 Android Studio 或命令行运行 Espresso 测试。请务必在项目中将 AndroidJUnitRunner 指定为默认插桩测试运行程序。

如需运行 Espresso 测试,请按照测试入门中所述的插桩测试运行步骤进行操作。

见文档:https://developer.android.google.cn/training/testing/unit-testing/instrumented-unit-tests?hl=zh-cn#run

3、UI Automator 测试框架

3.1简介

UI Automator 测试框架是基于插桩的 API,可与 AndroidJUnitRunner 测试运行程序一起使用。通过涉及多个应用中的用户交互的界面测试,可以验证当用户流跨入其他应用或系统界面时,的应用是否能够正常运行。短信应用就是此类用户流的一个例子,该应用先让用户输入短信,再启动 Android 联系人选择器,以便用户可以选择短信的收件人,然后将控制权返还给原来的应用,以便用户提交短信。

使用 AndroidX Test 提供的 UI Automator 测试框架来编写此类界面测试。通过 UI Automator API,可以与设备上的可见元素进行交互,而不管焦点在哪个 Activity 上。的测试可以使用方便的描述符(如显示在相应组件中的文本或其内容描述)来查找界面组件。UI Automator 测试可以在搭载 Android 4.3(API 级别 18)或更高版本的设备上运行。

3.2设置 UI Automator

在使用 UI Automator 构建界面测试之前,请务必配置测试源代码位置和项目依赖项,如针对 AndroidX Test 设置项目中所述。

在 Android 应用模块的 build.gradle 文件中,必须设置对 UI Automator 库的依赖项引用:

dependencies {

...

androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'

}

要优化 UI Automator 测试,应先检查目标应用的界面组件并确保它们可访问。这些优化提示将在接下来的两部分中进行介绍。

3.3检查设备上的界面

在设计测试之前,请先检查设备上可见的界面组件。要确保 UI Automator 测试可以访问这些组件,请检查这些组件是否具有可见文本标签和/或 android:contentDescription 值。

uiautomatorviewer 工具提供了一个方便的可视界面,用于检查布局层次结构以及查看在设备前台显示的界面组件的属性。利用此信息,可以使用 UI Automator 创建更精细的测试。例如,可以创建与特定可见属性匹配的界面选择器。

要启动 uiautomatorviewer 工具,请执行以下操作:

在实体设备上启动目标应用。

将设备连接到开发机器。

打开终端窗口并导航至 <android-sdk>/tools/ 目录。

使用以下命令运行该工具:

$ uiautomatorviewer

如需查看应用的界面属性,请执行以下操作:

在 uiautomatorviewer 界面中,点击 Device Screenshot 按钮。

将鼠标悬停在左侧面板中的快照上,以查看 uiautomatorviewer 工具识别的界面组件。右下方的面板中列出了属性,右上方的面板中列出了布局层次结构。

(可选)点击 Toggle NAF Nodes 按钮,以查看 UI Automator 无法访问的界面组件。对于这些组件,系统显示的相关信息可能很有限。

3.4确保 Activity 可访问

UI Automator 测试框架在已实现 Android 无障碍功能的应用上效果更好。当使用类型为 View 或 SDK 中的 View 的子类的界面元素时,无需实现无障碍功能支持,因为这些类已经为实现了这项支持。

不过,某些应用会使用自定义界面元素来提供更丰富的用户体验。此类元素不会提供自动无障碍功能支持。如果的应用包含不是 SDK 中的 View 的子类的实例,请务必向这些元素添加无障碍功能,具体操作步骤如下:

创建一个扩展 ExploreByTouchHelper 的具体类。

通过调用 setAccessibilityDelegate(),将新类的实例与特定自定义界面元素相关联。

如需获得有关向自定义视图元素添加无障碍功能的其他指导,请参阅构建无障碍自定义视图。如需详细了解 Android 平台上无障碍功能的常规最佳做法,请参阅改进应用的无障碍功能。

3.5创建 UI Automator 测试类

UI Automator 测试类的编写方式应与 JUnit 4 测试类相同。如需详细了解如何创建 JUnit 4 测试类以及如何使用 JUnit 4 断言和注释,请参阅创建插桩单元测试类。

在测试类定义的开头添加 @RunWith(AndroidJUnit4.class) 注释。还需要将 AndroidX Test 中提供的 AndroidJUnitRunner 类指定为默认测试运行程序。在设备或模拟器上运行 UI Automator 测试部分对此步骤进行了更详细的说明。

在 UI Automator 测试类中实现以下编程模型:

通过调用 getInstance() 方法并将 Instrumentation 对象作为参数传递给该方法,获取 UiDevice 对象以访问要测试的设备。

通过调用 findObject() 方法,获取 UiObject 对象以访问设备上显示的界面组件(例如,前台的当前视图)。

通过调用 UiObject 方法,模拟需要在该界面组件上执行的特定用户交互;例如,调用 performMultiPointerGesture() 以模拟多点触控手势,以及调用 setText() 以修改文本字段。可以根据需要反复调用第 2 步和第 3 步中的 API,以测试涉及多个界面组件或用户操作序列的更复杂的用户交互。

执行这些用户交互后,检查界面是否反映了预期的状态或行为。

下面几部分更详细地介绍了这些步骤。

3.6访问界面组件

UiDevice 对象是访问和操纵设备状态的主要方式。在测试中,可以调用 UiDevice 方法检查各种属性的状态,如当前屏幕方向或显示屏尺寸。的测试可以使用 UiDevice 对象执行设备级操作,如强制设备进行特定旋转、按方向键硬件按钮,以及按主屏幕和菜单按钮。

最好从设备的主屏幕开始测试。在主屏幕(或在设备中选择的其他某个起始位置)上,可以调用 UI Automator API 提供的方法,以选择特定的界面元素并与之交互。

在某些情况测试中,例如@SdkSuppress(minSdkVersion = 18) 语句有助于确保测试只能在搭载 Android 4.3(API 级别 18)或更高版本的设备上运行(根据 Android Automator 框架的要求)。

使用 findObject() 方法检索 UiObject,它表示符合给定选择器条件的视图。可以根据需要重复使用已在应用测试的其他部分中创建的 UiObject 实例。请注意,每当的测试使用 UiObject 实例以点击界面元素或查询属性时,UI Automator 测试框架都会在当前显示内容中搜索匹配项。

3.7指定选择器

如果需要访问应用中的特定界面组件,请使用 UiSelector 类。此类表示对当前显示的界面中特定元素的查询。

如果找到了多个匹配元素,系统会将布局层次结构中的第一个匹配元素作为目标 UiObject 返回。构建 UiSelector 时,可以将多个属性链接在一起以优化搜索。如果未找到匹配的界面元素,系统会抛出 UiAutomatorObjectNotFoundException。

可以使用 childSelector() 方法来嵌套多个 UiSelector 个实例。

最佳做法是,在指定选择器时,应使用资源 ID(如果已将其分配给界面元素),而不是文本元素或内容描述符。并非所有元素都有文本元素(例如,工具栏中的图标)。文本选择器很脆弱,如果界面发生细微更改,可能会导致测试失败。此外,文本选择器也可能无法在不同语言之间扩展,它们可能与翻译的字符串不匹配。

在选择器条件中指定对象状态可能很有用。例如,如果要选择所有已选中元素的列表以便取消选中这些元素,请调用 checked() 方法并将参数设置为 true。

3.8执行操作

的测试获取 UiObject 对象后,可以调用 UiObject 类中的方法,在由该对象表示的界面组件上执行用户交互。可以指定如下操作:

click():点击界面元素的可见边界的中心。

dragTo():将此对象拖动到任意坐标。

setText():清除可修改字段的内容后,设置该字段中的文本。相反,clearTextField() 方法用于清除可修改字段中的现有文本。

swipeUp():对 UiObject 执行向上滑动操作。同样,swipeDown()、swipeLeft() 和 swipeRight() 方法用于执行相应的操作。

通过 UI Automator 测试框架,可以发送 Intent 或启动 Activity,无需使用 shell 命令,只需通过 getContext() 获取 Context 对象即可。

3.8.1对集合执行操作

如果需要模拟内容集合(例如,音乐专辑中的歌曲或收件箱中的电子邮件列表)上的用户交互,请使用 UiCollection 类。要创建 UiCollection 对象,请指定 UiSelector,用于搜索其他子界面元素的界面容器或封装容器,如包含子界面元素的布局视图。

3.8.2对可滚动视图执行操作

使用 UiScrollable 类模拟显示屏上的垂直或水平滚动。当界面元素位于屏幕外而需要滚动屏幕以使其进入视野时,此方法很有用。

3.9验证结果

InstrumentationTestCase 扩展了 TestCase,因此可以使用标准的 JUnit Assert 方法测试应用中的界面组件是否会返回预期结果。

3.10在设备或模拟器上运行 UI Automator 测试

可以通过 Android Studio 或命令行运行 UI Automator 测试。请务必在项目中将 AndroidJUnitRunner 指定为默认插桩测试运行程序。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335