测试 Cupcake 应用

1. 简介

使用 Compose 在屏幕之间导航 代码实验室中,您学习了如何使用 Jetpack Navigation Compose 组件向 Compose 应用添加导航。

Cupcake 应用有多个屏幕需要导航,并且用户可以执行各种操作。此应用为您提供了磨练自动化测试技能的绝佳机会!在此代码实验室中,您将为 Cupcake 应用编写许多 UI 测试,并学习如何最大限度地提高测试覆盖率。

先决条件

您将学到什么

  • 使用 Compose 测试 Jetpack Navigation 组件。
  • 为每个 UI 测试创建一致的 UI 状态。
  • 为测试创建辅助函数。

您将构建什么

  • Cupcake 应用的 UI 测试

您需要什么

  • 最新版本的 Android Studio
  • 用于下载初始代码的互联网连接

2. 下载初始代码

  1. 在 Android Studio 中,打开 basic-android-kotlin-compose-training-cupcake 文件夹。
  2. 在 Android Studio 中打开 Cupcake 应用代码。

3. 为 UI 测试设置 Cupcake

添加 androidTest 依赖项

Gradle 构建工具使您能够为特定模块添加依赖项。此功能可防止不必要地编译依赖项。在项目中包含依赖项时,您已熟悉 implementation 配置。您已使用此关键字在应用模块的 build.gradle.kts 文件中导入依赖项。使用 implementation 关键字使该依赖项可用于该模块中的所有源集;在本课程的这一点上,您获得了 maintestandroidTest 源集的经验。

UI 测试包含在其自己的名为 androidTest 的源集中。仅此模块需要的依赖项不需要为其他模块(例如包含应用代码的 main 模块)编译。在添加仅由 UI 测试使用的依赖项时,请使用 androidTestImplementation 关键字在应用模块的 build.gradle.kts 文件中声明该依赖项。这样做可确保仅在运行 UI 测试时编译 UI 测试依赖项。

完成以下步骤以添加编写 UI 测试所需的依赖项

  1. 打开 build.gradle.kts(Module :app) 文件。
  2. 将以下依赖项添加到文件的 dependencies 部分
androidTestImplementation(platform("androidx.compose:compose-bom:2023.05.01"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
androidTestImplementation("androidx.navigation:navigation-testing:2.6.0")
androidTestImplementation("androidx.test.espresso:espresso-intents:3.5.1")
androidTestImplementation("androidx.test.ext:junit:1.1.5")

创建 UI 测试目录

  1. 右键单击项目视图中的 src 目录,然后选择新建>目录

290b1ab99ce8f4e5.png

  1. 选择androidTest/java选项。

718bde5eb21b4f6f.png

创建测试包

  1. 右键单击项目窗口中的 androidTest/java 目录,然后选择新建>

7ad315f41e2b0881.png

  1. 将包命名为com.example.cupcake.testb8306f9d11988a2.png

创建导航测试类

test 目录中,创建一个名为 CupcakeScreenNavigationTest 的新 Kotlin 类。

5515db35968f7d86.png

4b527e7404fad927.png

4. 设置导航宿主

在之前的代码实验室中,您了解到 Compose 中的 UI 测试需要一个 Compose 测试规则。测试 Jetpack Navigation 也是如此。但是,测试导航需要通过 Compose 测试规则进行一些额外的设置。

在测试 Compose Navigation 时,您将无法访问与应用代码中相同的 NavHostController。但是,您可以使用 TestNavHostController 并使用此导航控制器配置测试规则。在本节中,您将学习如何配置和重用用于导航测试的测试规则。

  1. CupcakeScreenNavigationTest.kt 中,使用 createAndroidComposeRule 创建一个测试规则,并将 ComponentActivity 作为类型参数传递。
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import org.junit.Rule

@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()

为了确保您的应用导航到正确的位置,您需要引用 TestNavHostController 实例以在应用采取导航操作时检查导航宿主的导航路线。

  1. TestNavHostController 实例实例化为一个 lateinit 变量。在 Kotlin 中,lateinit 关键字用于声明可以在对象声明后初始化的属性。
import androidx.navigation.testing.TestNavHostController

private lateinit var navController: TestNavHostController

接下来,指定要用于 UI 测试的可组合项。

  1. 创建一个名为 setupCupcakeNavHost() 的方法。
  2. setupCupcakeNavHost() 方法中,对您创建的 Compose 测试规则调用 setContent() 方法。
  3. 在传递给 setContent() 方法的 lambda 内部,调用 CupcakeApp() 可组合项。
import com.example.cupcake.CupcakeApp

fun setupCupcakeNavHost() {
    composeTestRule.setContent {
        CupcakeApp()
    }
}

现在需要在测试类中创建 TestNavHostContoller 对象。稍后将使用此对象确定导航状态,因为应用使用该控制器导航 Cupcake 应用中的各个屏幕。

  1. 使用之前创建的 lambda 设置导航宿主。初始化您创建的 navController 变量,注册一个导航器,并将该 TestNavHostController 传递给 CupcakeApp 可组合项。
import androidx.compose.ui.platform.LocalContext

fun setupCupcakeNavHost() {
    composeTestRule.setContent {
        navController = TestNavHostController(LocalContext.current).apply {
            navigatorProvider.addNavigator(ComposeNavigator())
        }
        CupcakeApp(navController = navController)
    }
}

CupcakeScreenNavigationTest 类中的每个测试都涉及测试导航的一个方面。因此,每个测试都依赖于您创建的 TestNavHostController 对象。无需为每个测试手动调用 setupCupcakeNavHost() 函数来设置导航控制器,您可以使用 junit 库提供的 @Before 注解自动执行此操作。当方法使用 @Before 进行注释时,它会在每个使用 @Test 进行注释的方法之前运行。

  1. @Before 注解添加到 setupCupcakeNavHost() 方法。
import org.junit.Before

@Before
fun setupCupcakeNavHost() {
    composeTestRule.setContent {
        navController = TestNavHostController(LocalContext.current).apply {
            navigatorProvider.addNavigator(ComposeNavigator())
        }
        CupcakeApp(navController = navController)
    }
}

5. 编写导航测试

验证起始目的地

回想一下,在构建 Cupcake 应用时,您创建了一个名为 CupcakeScreenenum 类,其中包含用于指示应用导航的常量。

CupcakeScreen.kt

/**
* enum values that represent the screens in the app
*/
enum class CupcakeScreen(@StringRes val title: Int) {
   Start(title = R.string.app_name),
   Flavor(title = R.string.choose_flavor),
   Pickup(title = R.string.choose_pickup_date),
   Summary(title = R.string.order_summary)
}

所有具有 UI 的应用都具有一定类型的主屏幕。对于 Cupcake,该屏幕是开始订单屏幕CupcakeApp 可组合项中的导航控制器使用 CupcakeScreen 枚举的 Start 项来确定何时导航到此屏幕。应用启动时,如果目的地路线尚不存在,则导航宿主目的地路线将设置为 CupcakeScreen.Start.name

首先需要编写一个测试来验证应用启动时开始订单屏幕是否为当前目的地路线。

  1. 创建一个名为 cupcakeNavHost_verifyStartDestination() 的函数,并使用 @Test 进行注释。
import org.junit.Test

@Test
fun cupcakeNavHost_verifyStartDestination() {
}

现在必须确认导航控制器的初始目的地路线是开始订单屏幕

  1. 断言预期路线名称(在本例中为 CupcakeScreen.Start.name)等于导航控制器的当前回退栈条目的目的地路线。
import org.junit.Assert.assertEquals
...

@Test
fun cupcakeNavHost_verifyStartDestination() {
    assertEquals(CupcakeScreen.Start.name, navController.currentBackStackEntry?.destination?.route)
}

创建辅助方法

UI 测试通常需要重复执行步骤以将 UI 置于可以测试 UI 特定部分的状态。自定义 UI 也可能需要包含多行代码的复杂断言。您在上节中编写的断言需要大量代码,并且在测试 Cupcake 应用中的导航时,您多次使用此断言。在这些情况下,在测试中编写辅助方法可以避免编写重复代码。

对于您编写的每个导航测试,您都使用 CupcakeScreen 枚举项的 name 属性来检查导航控制器的当前目的地路线是否正确。您将编写一个辅助函数,以便在需要进行此类断言时调用它。

完成以下步骤以创建此辅助函数

  1. test 目录中创建一个名为 ScreenAssertions 的空 Kotlin 文件。

63af08bd78a827c4.png

  1. NavController 类添加一个扩展函数,名为 assertCurrentRouteName(),并在方法签名中传递一个表示预期路由名称的字符串。
fun NavController.assertCurrentRouteName(expectedRouteName: String) {

}
  1. 在这个函数中,断言 expectedRouteName 等于导航控制器当前回退栈条目目标路由。
import org.junit.Assert.assertEquals
...

fun NavController.assertCurrentRouteName(expectedRouteName: String) {
    assertEquals(expectedRouteName, currentBackStackEntry?.destination?.route)
}
  1. 打开 CupcakeScreenNavigationTest 文件,修改 cupcakeNavHost_verifyStartDestination() 函数,使用新的扩展函数替换冗长的断言。
@Test
fun cupcakeNavHost_verifyStartDestination() {
    navController.assertCurrentRouteName(CupcakeScreen.Start.name)
}

许多测试还需要与 UI 组件交互。在本 Codelab 中,这些组件通常使用资源字符串查找。您可以使用 Context.getString() 方法通过其资源字符串访问一个可组合项,您可以在 此处 阅读相关信息。在 Compose 中编写 UI 测试时,实现此方法如下所示

composeTestRule.onNodeWithText(composeTestRule.activity.getString(R.string.my_string)

这是一个冗长的指令,可以通过添加扩展函数来简化。

  1. com.example.cupcake.test 包中创建一个名为 ComposeRuleExtensions.kt 的新文件。确保这是一个纯 Kotlin 文件。

5710fc2d8219048b.png

  1. 将以下代码添加到该文件中。
import androidx.activity.ComponentActivity
import androidx.annotation.StringRes
import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.ext.junit.rules.ActivityScenarioRule

fun <A : ComponentActivity> AndroidComposeTestRule<ActivityScenarioRule<A>, A>.onNodeWithStringId(
    @StringRes id: Int
): SemanticsNodeInteraction = onNodeWithText(activity.getString(id))

此扩展函数允许您在通过字符串资源查找 UI 组件时减少代码量。无需编写以下代码:

composeTestRule.onNodeWithText(composeTestRule.activity.getString(R.string.my_string)

您现在可以使用以下指令:

composeTestRule.onNodeWithStringId(R.string.my_string)

验证开始屏幕没有向上按钮。

Cupcake 应用的原始设计在开始屏幕的工具栏中没有向上按钮。

ffb8d156220c7e57.png

开始屏幕缺少按钮,因为从该屏幕没有地方可以向上导航,因为它是最开始的屏幕。请按照以下步骤创建一个函数,确认开始屏幕没有向上按钮。

  1. 创建一个名为 cupcakeNavHost_verifyBackNavigationNotShownOnStartOrderScreen() 的方法,并使用 @Test 注解它。
@Test
fun cupcakeNavHost_verifyBackNavigationNotShownOnStartOrderScreen() {
}

在 Cupcake 中,向上按钮的内容描述设置为来自 R.string.back_button 资源的字符串。

  1. 在测试函数中创建一个变量,其值为 R.string.back_button 资源。
@Test
fun cupcakeNavHost_verifyBackNavigationNotShownOnStartOrderScreen() {
    val backText = composeTestRule.activity.getString(R.string.back_button)
}
  1. 断言屏幕上不存在具有此内容描述的节点。
@Test
fun cupcakeNavHost_verifyBackNavigationNotShownOnStartOrderScreen() {
    val backText = composeTestRule.activity.getString(R.string.back_button)
    composeTestRule.onNodeWithContentDescription(backText).assertDoesNotExist()
}

验证导航到口味屏幕。

点击开始屏幕中的其中一个按钮会触发一个方法,该方法指示导航控制器导航到口味屏幕。

3bda045bfe202c90.png

在此测试中,您编写一条命令来点击一个按钮以触发此导航,并验证目标路由是否为口味屏幕。

  1. 创建一个名为 cupcakeNavHost_clickOneCupcake_navigatesToSelectFlavorScreen() 的函数,并使用 @Test 注解它。
@Test
fun cupcakeNavHost_clickOneCupcake_navigatesToSelectFlavorScreen(){
}
  1. 通过其字符串资源 ID 查找“一个纸杯蛋糕”按钮,并对其执行点击操作。
import com.example.cupcake.R
...

@Test
fun cupcakeNavHost_clickOneCupcake_navigatesToSelectFlavorScreen() {
    composeTestRule.onNodeWithStringId(R.string.one_cupcake)
        .performClick()
}
  1. 断言当前路由名称是口味屏幕名称。
@Test
fun cupcakeNavHost_clickOneCupcake_navigatesToSelectFlavorScreen() {
    composeTestRule.onNodeWithStringId(R.string.one_cupcake)
        .performClick()
    navController.assertCurrentRouteName(CupcakeScreen.Flavor.name)
}

编写更多辅助方法。

Cupcake 应用具有大部分线性的导航流程。除了点击“取消”按钮之外,您只能沿一个方向浏览应用。因此,当您测试应用中更深层的屏幕时,您可能会发现自己重复编写代码以导航到要测试的区域。这种情况需要使用更多辅助方法,以便您只需编写一次该代码。

既然您已经测试了导航到口味屏幕,请创建一个导航到口味屏幕的方法,以便您无需在以后的测试中重复编写该代码。

  1. 创建一个名为 navigateToFlavorScreen() 的方法。
private fun navigateToFlavorScreen() {
}
  1. 编写一条命令来查找“一个纸杯蛋糕”按钮,并对其执行点击操作,就像您在上一节中所做的那样。
private fun navigateToFlavorScreen() {
    composeTestRule.onNodeWithStringId(R.string.one_cupcake)
        .performClick()
}

回想一下,口味屏幕上的“下一步”按钮在选择口味之前是不可点击的。此方法仅用于准备 UI 进行导航。调用此方法后,UI 应处于“下一步”按钮可点击的状态。

  1. 在 UI 中查找一个具有 R.string.chocolate 字符串的节点,并对其执行点击操作以进行选择。
private fun navigateToFlavorScreen() {
    composeTestRule.onNodeWithStringId(R.string.one_cupcake)
        .performClick()
    composeTestRule.onNodeWithStringId(R.string.chocolate)
        .performClick()
}

看看您是否可以编写辅助方法来导航到取货屏幕和摘要屏幕。在查看解决方案之前,请先尝试自己完成此练习。

使用以下代码来完成此操作。

private fun getFormattedDate(): String {
    val calendar = Calendar.getInstance()
    calendar.add(java.util.Calendar.DATE, 1)
    val formatter = SimpleDateFormat("E MMM d", Locale.getDefault())
    return formatter.format(calendar.time)
}
private fun navigateToPickupScreen() {
    navigateToFlavorScreen()
    composeTestRule.onNodeWithStringId(R.string.next)
        .performClick()
}

private fun navigateToSummaryScreen() {
    navigateToPickupScreen()
    composeTestRule.onNodeWithText(getFormattedDate())
        .performClick()
    composeTestRule.onNodeWithStringId(R.string.next)
        .performClick()
}

当您测试开始屏幕以外的屏幕时,您需要计划测试向上按钮的功能,以确保它将导航引导至上一屏幕。考虑创建一个辅助函数来查找和点击向上按钮。

private fun performNavigateUp() {
    val backText = composeTestRule.activity.getString(R.string.back_button)
    composeTestRule.onNodeWithContentDescription(backText).performClick()
}

最大化测试覆盖率。

应用的测试套件应尽可能多地测试应用功能。在理想情况下,UI 测试套件将覆盖 100% 的 UI 功能。在实践中,很难达到这种测试覆盖率,因为有许多外部因素会影响您的应用 UI,例如具有独特屏幕尺寸的设备、不同版本的 Android 操作系统以及可能影响手机上其他应用的第三方应用。

帮助最大化测试覆盖率的一种方法是在添加功能时并行编写测试。这样做可以避免在新的功能上投入过多时间,然后不得不回过头来回忆所有可能的情况。Cupcake 在此时是一个相当小的应用,您已经测试了应用导航的大部分内容!但是,还有更多导航状态需要测试。

看看您是否可以编写测试来验证以下导航状态。在查看解决方案之前,请尝试自己实现它们。

  • 从口味屏幕点击向上按钮导航到开始屏幕。
  • 从口味屏幕点击取消按钮导航到开始屏幕。
  • 导航到取货屏幕。
  • 从取货屏幕点击向上按钮导航到口味屏幕。
  • 从取货屏幕点击取消按钮导航到开始屏幕。
  • 导航到摘要屏幕。
  • 从摘要屏幕点击取消按钮导航到开始屏幕。
@Test
fun cupcakeNavHost_clickNextOnFlavorScreen_navigatesToPickupScreen() {
    navigateToFlavorScreen()
    composeTestRule.onNodeWithStringId(R.string.next)
        .performClick()
    navController.assertCurrentRouteName(CupcakeScreen.Pickup.name)
}

@Test
fun cupcakeNavHost_clickBackOnFlavorScreen_navigatesToStartOrderScreen() {
    navigateToFlavorScreen()
    performNavigateUp()
    navController.assertCurrentRouteName(CupcakeScreen.Start.name)
}

@Test
fun cupcakeNavHost_clickCancelOnFlavorScreen_navigatesToStartOrderScreen() {
    navigateToFlavorScreen()
    composeTestRule.onNodeWithStringId(R.string.cancel)
        .performClick()
    navController.assertCurrentRouteName(CupcakeScreen.Start.name)
}

@Test
fun cupcakeNavHost_clickNextOnPickupScreen_navigatesToSummaryScreen() {
    navigateToPickupScreen()
    composeTestRule.onNodeWithText(getFormattedDate())
        .performClick()
    composeTestRule.onNodeWithStringId(R.string.next)
        .performClick()
    navController.assertCurrentRouteName(CupcakeScreen.Summary.name)
}

@Test
fun cupcakeNavHost_clickBackOnPickupScreen_navigatesToFlavorScreen() {
    navigateToPickupScreen()
    performNavigateUp()
    navController.assertCurrentRouteName(CupcakeScreen.Flavor.name)
}

@Test
fun cupcakeNavHost_clickCancelOnPickupScreen_navigatesToStartOrderScreen() {
    navigateToPickupScreen()
    composeTestRule.onNodeWithStringId(R.string.cancel)
        .performClick()
    navController.assertCurrentRouteName(CupcakeScreen.Start.name)
}

@Test
fun cupcakeNavHost_clickCancelOnSummaryScreen_navigatesToStartOrderScreen() {
    navigateToSummaryScreen()
    composeTestRule.onNodeWithStringId(R.string.cancel)
        .performClick()
    navController.assertCurrentRouteName(CupcakeScreen.Start.name)
}

6. 为订单屏幕编写测试。

导航只是 Cupcake 应用功能的一个方面。用户还会与应用的每个屏幕进行交互。您需要验证这些屏幕上显示的内容以及在这些屏幕上执行的操作是否产生了正确的结果。**SelectOptionScreen** 是应用的重要组成部分。

在本节中,您将编写一个测试来验证此屏幕上的内容是否正确设置。

测试选择口味屏幕内容。

  1. app/src/androidTest 目录中创建一个新类,名为 CupcakeOrderScreenTest,您的其他测试文件也位于该目录中。

1aaa3be367a02dcd.png

  1. 在此类中,创建一个 AndroidComposeTestRule
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
  1. 创建一个名为 selectOptionScreen_verifyContent() 的函数,并使用 @Test 注解它。
@Test
fun selectOptionScreen_verifyContent() {

}

在此函数中,您最终将 Compose 规则内容设置为 SelectOptionScreen。这样做可以确保 SelectOptionScreen 可组合项直接启动,从而无需导航。但是,此屏幕需要两个参数:口味选项列表和小计。

  1. 创建一个口味选项列表和小计,将它们传递给屏幕。
@Test
fun selectOptionScreen_verifyContent() {
    // Given list of options
    val flavors = listOf("Vanilla", "Chocolate", "Hazelnut", "Cookie", "Mango")
    // And subtotal
    val subtotal = "$100"
}
  1. 使用您刚刚创建的值将内容设置为 SelectOptionScreen 可组合项。

请注意,此方法类似于从 MainActivity 启动可组合项。唯一的区别是 MainActivity 调用 CupcakeApp 可组合项,而在这里您正在调用 SelectOptionScreen 可组合项。能够更改从 setContent() 启动的可组合项,使您可以启动特定的可组合项,而不是让测试明确地逐步遍历应用以到达要测试的区域。此方法有助于防止测试在与当前测试无关的代码区域中失败。

@Test
fun selectOptionScreen_verifyContent() {
    // Given list of options
    val flavors = listOf("Vanilla", "Chocolate", "Hazelnut", "Cookie", "Mango")
    // And subtotal
    val subtotal = "$100"

    // When SelectOptionScreen is loaded
    composeTestRule.setContent {
        SelectOptionScreen(subtotal = subtotal, options = flavors)
    }
}

在测试的这一点上,应用启动了 SelectOptionScreen 可组合项,然后您可以通过测试指令与之交互。

  1. 遍历 flavors 列表,并确保列表中的每个字符串项都显示在屏幕上。
  2. 使用 onNodeWithText() 方法查找屏幕上的文本,并使用 assertIsDisplayed() 方法验证文本是否显示在应用中。
@Test
fun selectOptionScreen_verifyContent() {
    // Given list of options
    val flavors = listOf("Vanilla", "Chocolate", "Hazelnut", "Cookie", "Mango")
    // And subtotal
    val subtotal = "$100"

    // When SelectOptionScreen is loaded
    composeTestRule.setContent {
        SelectOptionScreen(subtotal = subtotal, options = flavors)
    }

    // Then all the options are displayed on the screen.
    flavors.forEach { flavor ->
        composeTestRule.onNodeWithText(flavor).assertIsDisplayed()
    }
}
  1. 使用相同的技术来验证应用是否显示文本,验证应用是否在屏幕上显示正确的小计字符串。在屏幕上搜索 R.string.subtotal_price 资源 ID 和正确的小计值,然后断言应用是否显示该值。
import com.example.cupcake.R
...

@Test
fun selectOptionScreen_verifyContent() {
    // Given list of options
    val flavors = listOf("Vanilla", "Chocolate", "Hazelnut", "Cookie", "Mango")
    // And subtotal
    val subtotal = "$100"

    // When SelectOptionScreen is loaded
    composeTestRule.setContent {
        SelectOptionScreen(subtotal = subtotal, options = flavors)
    }

    // Then all the options are displayed on the screen.
    flavors.forEach { flavor ->
        composeTestRule.onNodeWithText(flavor).assertIsDisplayed()
    }

    // And then the subtotal is displayed correctly.
    composeTestRule.onNodeWithText(
        composeTestRule.activity.getString(
            R.string.subtotal_price,
            subtotal
        )
    ).assertIsDisplayed()
}

回想一下,“下一步”按钮在选择项目之前是未启用的。此测试仅验证屏幕内容,因此要测试的最后一件事是“下一步”按钮是否已禁用。

  1. 使用相同的方法通过字符串资源 ID 查找“下一步”按钮。但是,不要验证应用是否显示该节点,而是使用 assertIsNotEnabled() 方法。
@Test
fun selectOptionScreen_verifyContent() {
    // Given list of options
    val flavors = listOf("Vanilla", "Chocolate", "Hazelnut", "Cookie", "Mango")
    // And subtotal
    val subtotal = "$100"

    // When SelectOptionScreen is loaded
    composeTestRule.setContent {
        SelectOptionScreen(subtotal = subtotal, options = flavors)
    }

    // Then all the options are displayed on the screen.
    flavors.forEach { flavor ->
        composeTestRule.onNodeWithText(flavor).assertIsDisplayed()
    }

    // And then the subtotal is displayed correctly.
    composeTestRule.onNodeWithText(
        composeTestRule.activity.getString(
            R.string.subtotal_price,
            subtotal
        )
    ).assertIsDisplayed()

    // And then the next button is disabled
    composeTestRule.onNodeWithStringId(R.string.next).assertIsNotEnabled()
}

最大化测试覆盖率。

选择口味屏幕内容测试仅测试单个屏幕的一个方面。您可以编写许多其他测试来提高代码覆盖率。在下载解决方案代码之前,请尝试自己编写以下测试。

  • 验证开始屏幕内容。
  • 验证摘要屏幕内容。
  • 验证在选择口味屏幕上选择项目后,“下一步”按钮是否已启用。

在编写测试时,请记住任何可能减少代码量的辅助函数!

7. 获取解决方案代码。

要下载已完成 Codelab 的代码,您可以使用以下 git 命令。

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-cupcake.git

或者,您可以将存储库下载为 zip 文件,解压缩它,然后在 Android Studio 中打开它。

如果要查看解决方案代码,请在 GitHub 上查看

8. 总结。

恭喜!您已经学习了如何测试 Jetpack Navigation 组件。您还学习了一些编写 UI 测试的基本技能,例如编写可重用的辅助方法、如何利用 setContent() 编写简洁的测试、如何使用 @Before 注解设置测试以及如何考虑最大测试覆盖率。在继续构建 Android 应用时,请记住在功能代码旁边继续编写测试!