编写插桩测试

1. 准备工作

在上一个 Codelab 中,您学习了如何创建和运行单元测试。此 Codelab 将重点介绍插桩测试。您将了解插桩测试是什么样的,以及如何编写插桩测试。

前提条件

  • 已在 Android Studio 中创建过项目。
  • 具有一定的在 Android Studio 中编写代码的经验。
  • 具有一定的在 Android Studio 中浏览项目的经验。
  • 您在 Android Studio 中编写了简单的单元测试。

学习内容

  • 插桩测试是什么样的?
  • 如何运行插桩测试?
  • 如何编写插桩测试?

所需条件

  • 一台安装了 Android Studio 的计算机。
  • Tip Time 应用的解决方案代码。

下载此 Codelab 的起始代码

在此 Codelab 中,您需要将插桩测试从之前的解决方案代码添加到 Tip Time 应用中。

  1. 进入为此项目提供的 GitHub 代码库页面。
  2. 验证分支名称是否与此 Codelab 中指定的分支名称一致。例如,在以下屏幕截图中,分支名称为 main

1e4c0d2c081a8fd2.png

  1. 在项目的 GitHub 页面上,点击 Code 按钮,以打开一个弹出式窗口。

1debcf330fd04c7b.png

  1. 在弹出式窗口中,点击 Download ZIP 按钮,将项目保存到计算机上。等待下载完成。
  2. 在计算机上找到该文件(很可能在 Downloads 文件夹中)。
  3. 双击 ZIP 文件进行解压缩。系统将创建一个包含项目文件的新文件夹。

在 Android Studio 中打开项目

  1. 启动 Android Studio。
  2. Welcome to Android Studio 窗口中,点击 Open

d8e9dbdeafe9038a.png

注意:如果 Android Studio 已经打开,则改为依次选择 File > Open 菜单选项。

8d1fda7396afe8e5.png

  1. 在文件浏览器中,前往解压缩的项目文件夹所在的位置(很可能在 Downloads 文件夹中)。
  2. 双击该项目文件夹。
  3. 等待 Android Studio 打开项目。
  4. 点击 Run 按钮 8de56cba7583251f.png 以构建并运行应用。请确保该应用按预期构建。

2. 起始应用概览

Tip Time 应用包含一个屏幕,该屏幕向用户显示用于输入账单金额的文本输入项、用于选择小费百分比的单选按钮,以及用于计算小费的按钮。

3. 创建插桩测试目录

此 Codelab 的起始代码可完全正常运行,但缺少测试和测试目录。在编写任何类型的测试之前,我们都需要为插桩测试添加一个目录。下载起始代码后,请执行以下步骤为您的插桩测试添加类。

  1. 最简单的方法是先从 Android 视图切换到 Project 视图。在左上角的项目窗格中,点击显示 Android 的下拉菜单,然后选择 Project

20055afdc5d38e69.png

  1. 您的项目视图现在将如下所示:

7e933e8dd546458e.png

  1. 点击第一个下拉菜单,然后依次点击 app -> src

65dd6a80920623ef.png

  1. 右键点击 src,然后依次选择 New -> Directory

73b014c84f89b0f0.png

  1. 您应该会看到以下窗口:

921d8a0df6310383.png

  1. 选择 androidTest/java

c4b99c44611ae609.png

  1. 现在,您会在项目结构中看到 androidTest 目录。

5f6643d80a7ef0f8.png

  1. 右键点击 java 目录,然后依次选择 New -> Package

8f8a590b7bdc01b3.png

  1. 您将看到随即显示的窗口:

4d154746de968ccf.png

  1. 在该窗口中,输入以下文本,然后按 Return 键:
com.example.tiptime
  1. 您的项目窗口应如下所示:

7bb036a4bc3be441.png

  1. 最后,右键点击 com.example.tiptime,然后依次选择 New -> Kotlin Class/File

26ff162c120e18d1.png

  1. 在随即显示的窗口中,输入 CalculatorTests,从下拉菜单中选择 Class,然后按 Return 键。

4. 编写您的第一个插桩测试

现在可以编写插桩测试了。以下步骤可测试用于计算 20% 小费的功能。

  1. 打开刚刚创建的文件,它应如下所示:
package com.example.tiptime

class CalculatorTests {
}
  1. 插桩测试需要 InstrumentationTestRunner,以便在设备或模拟器上执行测试。还有一些其他的插桩运行程序,但对于此测试,我们将使用 AndroidJUnit4 测试运行程序。如需指定测试运行程序,我们需要为该类添加以下注解:
@RunWith(AndroidJUnit4::class)
class CalculatorTests {
}

为了以防万一,您需要以下导入内容:

import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith

现在,一切都已设置完毕,可以开始编写测试逻辑了。

  1. Tip Time 应用包含一个 activity,即 MainActivity。为了与该 activity 互动,您的测试用例必须先启动它。在 CalculatorTests 类中添加以下代码。
@get:Rule()
val activity = ActivityScenarioRule(MainActivity::class.java)

ActivityScenarioRule 来自 AndroidX Test 库。它会告知设备启动由开发者指定的 activity。您需要使用 @get:Rule 为此 activity 添加注解,@get:Rule() 会指定后续规则(在本例中为启动 activity)应该在该类中的每个测试之前执行。测试规则是进行测试的重要工具,最终您将学习如何编写自己的测试规则。

  1. 接下来,您需要编写测试逻辑本身。创建一个函数,将其命名为 calculate_20_percent_tip(),并使用 @Test 为该函数添加注解。
@Test
fun calculate_20_percent_tip() {
}

Espresso

本课程主要使用 Espresso 进行插桩测试。Espresso 是一个库,可直接用于使用 Android Studio 创建的 Android 项目,并可让您通过代码与界面组件互动。

此时,您可能已经注意到 Android Studio 有许多自动补全功能。使用 Espresso 时的难题之一是,如果库尚未导入,将无法自动补全相关方法。这会导致您在不研究文档的情况下难以浏览 Espresso 中的可用方法。在这些课程中,我们将为您提供完成测试所需的方法。

首先,您需要编写代码以在 Cost of Service 输入文本视图中输入账单金额。依次进入 app -> src -> main -> res -> layout -> activity_main.xml,您会看到 TextInputEditText 的 ID 为 cost_of_service_edit_text。复制 ID 名称,稍后我们会在测试中用到该名称。

a113fb63b50f7674.png

实现测试函数

现在,您可以在测试类的 calculate_20_percent_tip() 函数中编写测试逻辑。

  1. 第一步是使用 onView() 函数查找要与之互动的界面组件(本例中为 TextInputEditText)。onView() 函数接受 ViewMatcher 对象参数。ViewMatcher 本质上是一个符合特定条件的界面组件;在本例中是一个 ID 为 R.id.cost_of_service_edit_text 的组件。

函数 withId() 会返回 ViewMatcher,该界面组件具有传递给它的相应 ID。onView() 会返回 ViewInteraction,后者是一个对象,我们可以与该对象互动,就像是自己在控制设备一样。如需输入一些文本,您可以对 ViewInteraction 调用 perform()。然后,perform() 接受 ViewAction 对象。有许多方法会返回 ViewAction,但现在我们将使用 typeText() 方法。在 activity_main.xml 中,我们可以看到默认的小费选项是 20%,因此目前您无需指定选择哪种小费选项。

onView(withId(R.id.cost_of_service_edit_text))
    .perform(typeText("50.00"))

在此之后,整个指令将如下所示:

onView(withId(R.id.cost_of_service_edit_text))
    .perform(typeText("50.00"))
    .perform(ViewActions.closeSoftKeyboard())
  1. 现已输入文本,并且测试需要点击 Calculate 按钮。该步骤的代码遵循的格式与我们之前输入文本时采用的格式类似。界面组件不同,因此传递给 withId() 函数的 ID 名称也不同。但是,此方法的唯一区别是 ViewAction 不同;使用的是 click() 函数,而不是 typeText()
onView(withId(R.id.calculate_button))
    .perform(click())
  1. 最后,您需要断言会显示正确的小费。我们预计小费金额为 10.00 美元。对于此测试,请确保 ID 为 tip_resultTextView 包含字符串形式的预期小费值。
onView(withId(R.id.tip_result))
    .check(matches(withText(containsString("$10.00"))))

出现提示时,请选择以下 import:

import androidx.test.espresso.assertion.ViewAssertions.matches
import org.hamcrest.Matchers.containsString

在这里,您使用了名为 check() 的不同互动,它接受 ViewAssertion。您可以将 ViewAssertion 视为用于界面组件的特殊 Espresso 断言。断言是 TextView 的内容与包含字符串 "$10.00" 的文本匹配。

在运行测试之前,请确保 import 语句和代码正确无误,它们应如下所示(import 语句的顺序不同也没关系):

package com.example.tiptime

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.typeText
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.hamcrest.Matchers.containsString
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class CalculatorTests {

   @get:Rule()
   val activity = ActivityScenarioRule(MainActivity::class.java)

   @Test
   fun calculate_20_percent_tip() {
       onView(withId(R.id.cost_of_service_edit_text))
           .perform(typeText("50.00"))

       onView(withId(R.id.calculate_button)).perform(click())

       onView(withId(R.id.tip_result))
           .check(matches(withText(containsString("$10.00"))))
   }
}

如果您使用的是模拟器,请确保可以同时看到模拟器和 Android Studio 窗口。运行此测试,具体方式与运行单元测试(右键点击函数左侧的红色/绿色箭头按钮,然后选择第一个选项)一样,然后观察结果!

36684dfa8a17a2c9.gif

您可以看到,此测试在执行时就像有人在与应用本身进行互动一样!

5. 扩展测试套件

恭喜!您已成功运行首个插桩测试!

如果您准备好迎接挑战,可以通过添加用于测试其他小费百分比的函数来扩展测试套件。使用与我们编写的上述函数相同的格式,您只需编写代码以选择其他百分比选项,并更改传入 containsString() 的值以反映不同的预期结果。别忘了还有一个向上舍入的选项。您可以切换向上舍入开关,方法是按组件 ID 查找该组件(正如我们在 onView(withId()) 中展示的那样),然后点击它。

6. 解决方案代码

7. 总结

  • Android Studio 会在项目创建完毕后生成必要的测试类。但是,如果您遇到没有测试类的项目,可以手动创建。
  • 测试规则会在测试类中的每个测试之前运行。
  • Espresso 是插桩测试的基本组件。它支持使用代码与界面组件互动。

本课很长,大家做得非常棒,鼓励一下自己!

了解更多内容