1. 准备工作
在上一个 Codelab 中,您学习了如何创建和运行单元测试。此 Codelab 将重点介绍插桩测试。您将了解插桩测试是什么样的,以及如何编写插桩测试。
前提条件
- 已在 Android Studio 中创建过项目。
- 具有一定的在 Android Studio 中编写代码的经验。
- 具有一定的在 Android Studio 中浏览项目的经验。
- 您在 Android Studio 中编写了简单的单元测试。
学习内容
- 插桩测试是什么样的?
- 如何运行插桩测试?
- 如何编写插桩测试?
所需条件
- 一台安装了 Android Studio 的计算机。
- Tip Time 应用的解决方案代码。
下载此 Codelab 的起始代码
在此 Codelab 中,您需要将插桩测试从之前的解决方案代码添加到 Tip Time 应用中。
- 进入为此项目提供的 GitHub 代码库页面。
- 验证分支名称是否与此 Codelab 中指定的分支名称一致。例如,在以下屏幕截图中,分支名称为 main。
- 在项目的 GitHub 页面上,点击 Code 按钮,以打开一个弹出式窗口。
- 在弹出式窗口中,点击 Download ZIP 按钮,将项目保存到计算机上。等待下载完成。
- 在计算机上找到该文件(很可能在 Downloads 文件夹中)。
- 双击 ZIP 文件进行解压缩。系统将创建一个包含项目文件的新文件夹。
在 Android Studio 中打开项目
- 启动 Android Studio。
- 在 Welcome to Android Studio 窗口中,点击 Open。
注意:如果 Android Studio 已经打开,则改为依次选择 File > Open 菜单选项。
- 在文件浏览器中,前往解压缩的项目文件夹所在的位置(很可能在 Downloads 文件夹中)。
- 双击该项目文件夹。
- 等待 Android Studio 打开项目。
- 点击 Run 按钮 以构建并运行应用。请确保该应用按预期构建。
2. 起始应用概览
Tip Time 应用包含一个屏幕,该屏幕向用户显示用于输入账单金额的文本输入项、用于选择小费百分比的单选按钮,以及用于计算小费的按钮。
3. 创建插桩测试目录
此 Codelab 的起始代码可完全正常运行,但缺少测试和测试目录。在编写任何类型的测试之前,我们都需要为插桩测试添加一个目录。下载起始代码后,请执行以下步骤为您的插桩测试添加类。
- 最简单的方法是先从 Android 视图切换到 Project 视图。在左上角的项目窗格中,点击显示 Android 的下拉菜单,然后选择 Project。
- 您的项目视图现在将如下所示:
- 点击第一个下拉菜单,然后依次点击 app -> src。
- 右键点击 src,然后依次选择 New -> Directory。
- 您应该会看到以下窗口:
- 选择 androidTest/java。
- 现在,您会在项目结构中看到 androidTest 目录。
- 右键点击 java 目录,然后依次选择 New -> Package。
- 您将看到随即显示的窗口:
- 在该窗口中,输入以下文本,然后按 Return 键:
com.example.tiptime
- 您的项目窗口应如下所示:
- 最后,右键点击 com.example.tiptime,然后依次选择 New -> Kotlin Class/File。
- 在随即显示的窗口中,输入
CalculatorTests
,从下拉菜单中选择 Class,然后按 Return 键。
4. 编写您的第一个插桩测试
现在可以编写插桩测试了。以下步骤可测试用于计算 20% 小费的功能。
- 打开刚刚创建的文件,它应如下所示:
package com.example.tiptime
class CalculatorTests {
}
- 插桩测试需要 InstrumentationTestRunner,以便在设备或模拟器上执行测试。还有一些其他的插桩运行程序,但对于此测试,我们将使用
AndroidJUnit4
测试运行程序。如需指定测试运行程序,我们需要为该类添加以下注解:
@RunWith(AndroidJUnit4::class)
class CalculatorTests {
}
为了以防万一,您需要以下导入内容:
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
现在,一切都已设置完毕,可以开始编写测试逻辑了。
- Tip Time 应用包含一个 activity,即
MainActivity
。为了与该 activity 互动,您的测试用例必须先启动它。在CalculatorTests
类中添加以下代码。
@get:Rule()
val activity = ActivityScenarioRule(MainActivity::class.java)
ActivityScenarioRule
来自 AndroidX Test 库。它会告知设备启动由开发者指定的 activity。您需要使用 @get:Rule
为此 activity 添加注解,@get:Rule() 会指定后续规则(在本例中为启动 activity)应该在该类中的每个测试之前执行。测试规则是进行测试的重要工具,最终您将学习如何编写自己的测试规则。
- 接下来,您需要编写测试逻辑本身。创建一个函数,将其命名为
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 名称,稍后我们会在测试中用到该名称。
实现测试函数
现在,您可以在测试类的 calculate_20_percent_tip()
函数中编写测试逻辑。
- 第一步是使用
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())
- 现已输入文本,并且测试需要点击 Calculate 按钮。该步骤的代码遵循的格式与我们之前输入文本时采用的格式类似。界面组件不同,因此传递给
withId()
函数的 ID 名称也不同。但是,此方法的唯一区别是ViewAction
不同;使用的是click()
函数,而不是typeText()
。
onView(withId(R.id.calculate_button))
.perform(click())
- 最后,您需要断言会显示正确的小费。我们预计小费金额为 10.00 美元。对于此测试,请确保 ID 为
tip_result
的TextView
包含字符串形式的预期小费值。
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 窗口。运行此测试,具体方式与运行单元测试(右键点击函数左侧的红色/绿色箭头按钮,然后选择第一个选项)一样,然后观察结果!
您可以看到,此测试在执行时就像有人在与应用本身进行互动一样!
5. 扩展测试套件
恭喜!您已成功运行首个插桩测试!
如果您准备好迎接挑战,可以通过添加用于测试其他小费百分比的函数来扩展测试套件。使用与我们编写的上述函数相同的格式,您只需编写代码以选择其他百分比选项,并更改传入 containsString()
的值以反映不同的预期结果。别忘了还有一个向上舍入的选项。您可以切换向上舍入开关,方法是按组件 ID 查找该组件(正如我们在 onView(withId())
中展示的那样),然后点击它。
6. 解决方案代码
7. 总结
- Android Studio 会在项目创建完毕后生成必要的测试类。但是,如果您遇到没有测试类的项目,可以手动创建。
- 测试规则会在测试类中的每个测试之前运行。
- Espresso 是插桩测试的基本组件。它支持使用代码与界面组件互动。
本课很长,大家做得非常棒,鼓励一下自己!