测试 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 导航时,您将无法访问与应用代码中相同的 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 组件交互。在本代码实验室中,这些组件通常使用资源字符串查找。您可以使用 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()
}

验证导航到 Flavor 屏幕

点击开始屏幕上的某个按钮会触发一个方法,该方法指示导航控制器导航到 Flavor 屏幕。

3bda045bfe202c90.png

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

  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. 断言当前路由名称为 Flavor 屏幕名称。
@Test
fun cupcakeNavHost_clickOneCupcake_navigatesToSelectFlavorScreen() {
    composeTestRule.onNodeWithStringId(R.string.one_cupcake)
        .performClick()
    navController.assertCurrentRouteName(CupcakeScreen.Flavor.name)
}

编写更多辅助方法

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

既然您已经测试了导航到 Flavor 屏幕,请创建一个导航到 Flavor 屏幕的方法,以便您不必为将来的测试重复编写该代码。

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

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

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

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

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

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 在这一点上是一个相当小的应用,并且您已经测试了应用导航的很大一部分!但是,还有更多导航状态需要测试。

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

  • 通过从 Flavor 屏幕点击向上按钮导航到开始屏幕
  • 通过从 Flavor 屏幕点击取消按钮导航到开始屏幕
  • 导航到 Pickup 屏幕
  • 通过从 Pickup 屏幕点击向上按钮导航到 Flavor 屏幕
  • 通过从 Pickup 屏幕点击取消按钮导航到开始屏幕
  • 导航到 Summary 屏幕
  • 通过从 Summary 屏幕点击取消按钮导航到开始屏幕
@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()
}

最大化测试覆盖率

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

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

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

7. 获取解决方案代码

要下载已完成代码实验室的代码,您可以使用此 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 应用时,请记住在您的功能代码旁边继续编写测试!