Cómo probar la app de Cupcake

1. Introducción

En el codelab Cómo navegar entre pantallas con Compose, aprendiste a agregar la navegación a una app de Compose con el componente Navigation de Jetpack Compose.

La app de Cupcake tiene varias pantallas para navegar y una variedad de acciones que el usuario puede ejecutar. Esta app te brinda una gran oportunidad para perfeccionar tu habilidad de pruebas automatizadas. En este codelab, escribirás varias pruebas de IU para la app de Cupcake y aprenderás a abordar la maximización de la cobertura de pruebas.

Requisitos previos

Qué aprenderás

  • Probarás el componente Navigation de Jetpack con Compose.
  • Crearás un estado coherente de la IU para cada prueba de IU.
  • Crearás funciones auxiliares para las pruebas.

Qué compilarás

  • Pruebas de la IU de la app de Cupcake

Requisitos

  • La versión más reciente de Android Studio
  • Conexión a Internet para descargar el código de partida

2. Descarga el código de partida

  1. En Android Studio, abre la carpeta basic-android-kotlin-compose-training-cupcake.
  2. Abre el código de la app de Cupcake en Android Studio.

3. Cómo configurar Cupcake para las pruebas de IU

Cómo agregar las dependencias de androidTest

La herramienta de compilación de Gradle te permite agregar dependencias para módulos específicos. Esta funcionalidad evita que se compilen dependencias de forma innecesaria. Ya conoces la configuración de implementation a la hora de incluir dependencias en un proyecto. Usaste esta palabra clave para importar dependencias en el archivo build.gradle.kts del módulo de la app. El uso de la palabra clave implementation hace que esa dependencia esté disponible para todos los conjuntos de orígenes en ese módulo. En este momento del curso, ya tienes experiencia con los conjuntos de orígenes main, test y androidTest.

Las pruebas de IU se encuentran en sus propios conjuntos de orígenes llamados androidTest. Las dependencias que solo se necesitan para este módulo no necesitan compilarse en otros módulos, como main, que contiene el código de la app. Cuando agregues una dependencia que solo se use para las pruebas de IU, usa la palabra clave androidTestImplementation para declarar la dependencia en el archivo build.gradle.kts del módulo de la app. Esto garantiza que las dependencias de las pruebas de IU se compilen solo cuando ejecutes pruebas de IU.

Completa los siguientes pasos para agregar las dependencias necesarias para escribir pruebas de IU:

  1. Abre el archivo build.gradle.kts(Module :app).
  2. Agrega las siguientes dependencias a la sección dependencies del archivo:
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")

Cómo crear el directorio de prueba de la IU

  1. Haz clic con el botón derecho en el directorio src en la vista de proyecto y selecciona New > Directory.

31f950996e51807b.png

  1. Selecciona la opción androidTest/java.

e1e4d108c863ae27.png

Cómo crear el paquete de prueba

  1. Haz clic con el botón derecho en el directorio androidTest/java de la ventana del proyecto y selecciona New > Package.

c5cdd7125e61bf22.png

  1. Nombra el paquete com.example.cupcake.test. b6cec61624467422.png

Cómo crear la clase de prueba de navegación

En el directorio test, crea una nueva clase de Kotlin llamada CupcakeScreenNavigationTest.

8da2547b2ef1cc8e.png

4. Cómo configurar el host de navegación

En un codelab anterior, aprendiste que las pruebas de IU en Compose requieren una regla de prueba de Compose. Lo mismo sucede cuando se prueba Jetpack Navigation. Sin embargo, la navegación de prueba requiere una configuración adicional mediante la regla de prueba de Compose.

Cuando pruebes la Compose Navigation, no tendrás acceso al mismo NavHostController que tienes en el código de la app. Sin embargo, puedes usar un TestNavHostController y configurar la regla de prueba con este controlador de navegación. En esta sección, aprenderás a configurar y reutilizar la regla de prueba para las pruebas de navegación.

  1. En CupcakeScreenNavigationTest.kt, crea una regla de prueba con createAndroidComposeRule y pasa ComponentActivity como el parámetro de tipo.
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import org.junit.Rule

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

Para asegurarte de que tu app navegue al lugar correcto, debes hacer referencia a una instancia de TestNavHostController para que se verifique la ruta del host de navegación cuando la app realice acciones para navegar.

  1. Crea una instancia de TestNavHostController como una variable lateinit. En Kotlin, se usa la palabra clave lateinit para declarar una propiedad que se puede inicializar después de declarar el objeto.
import androidx.navigation.testing.TestNavHostController

private lateinit var navController: TestNavHostController

Luego, especifica el elemento componible que quieres usar para las pruebas de la IU.

  1. Crea un método llamado setupCupcakeNavHost().
  2. En el método setupCupcakeNavHost(), llama al método setContent() en la regla de prueba de Compose que creaste.
  3. Dentro de la lambda pasada al método setContent(), llama al elemento CupcakeApp() componible.
import com.example.cupcake.CupcakeApp

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

Ahora debes crear el objeto TestNavHostContoller en la clase de prueba. Usarás este objeto más adelante para determinar el estado de navegación, ya que la app usa el controlador a fin de navegar por las diferentes pantallas de la app de Cupcake.

  1. Configura el host de navegación mediante la lambda que creaste antes. Inicializa la variable navController que creaste, registra un navegador y pasa ese TestNavHostController al elemento CupcakeApp componible.
import androidx.compose.ui.platform.LocalContext

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

Cada prueba de la clase CupcakeScreenNavigationTest implica probar un aspecto de la navegación. Por lo tanto, cada prueba depende del objeto TestNavHostController que creaste. En lugar de tener que llamar manualmente a la función setupCupcakeNavHost() para cada prueba para configurar el controlador de navegación, puedes hacer que suceda automáticamente con la anotación @Before que proporciona la biblioteca junit. Cuando un método se anota con @Before, se ejecuta antes de cada método anotado con @Test.

  1. Agrega la anotación @Before al método setupCupcakeNavHost().
import org.junit.Before

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

5. Cómo escribir pruebas de navegación

Cómo verificar el destino de inicio

Recuerda que, cuando compilaste la app de Cupcake, creaste una clase enum llamada CupcakeScreen que contenía constantes para determinar la navegación de la app.

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)
}

Todas las apps que tienen una IU tienen una pantalla principal de algún tipo. Para Cupcake, esa pantalla es la pantalla de inicio del pedido. El controlador de navegación del elemento CupcakeApp componible usa el elemento Start del elemento CupcakeScreen de tipo enum para determinar cuándo navegar a esta pantalla. Cuando se inicia la app, si no existe una ruta de destino, la correspondiente al host de navegación se establece en CupcakeScreen.Start.name.

Primero, debes escribir una prueba para verificar que la pantalla de inicio del pedido sea la ruta de destino actual cuando se inicia la app.

  1. Crea una función llamada cupcakeNavHost_verifyStartDestination() y anótala con @Test.
import org.junit.Test

@Test
fun cupcakeNavHost_verifyStartDestination() {
}

Ahora debes confirmar que la ruta de destino inicial del controlador de navegación sea la Start Order Screen.

  1. Confirma que el nombre de ruta esperado (en este caso, CupcakeScreen.Start.name) sea igual a la ruta de destino de la entrada actual de la pila de actividades del controlador de navegación.
import org.junit.Assert.assertEquals
...

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

Cómo crear métodos auxiliares

Las pruebas de IU a menudo requieren la repetición de pasos para colocar la IU en un estado en el que se pueda probar una parte específica de ella. Una IU personalizada también puede requerir aserciones complejas que necesiten varias líneas de código. La aserción que escribiste en la sección anterior requiere mucho código, y la utilizas muchas veces cuando pruebas la navegación en la app de Cupcake. En estas situaciones, escribir métodos auxiliares en tus pruebas evita que escribas código duplicado.

Para cada prueba de navegación que escribas, debes usar la propiedad name de los elementos CupcakeScreen de tipo enum a fin de verificar que la ruta de destino actual del controlador de navegación sea la correcta. Debes escribir una función auxiliar a la que puedas llamar cada vez que desees realizar esa aserción.

Para crear esta función auxiliar, completa los siguientes pasos:

  1. Crea un archivo Kotlin vacío en el directorio test llamado ScreenAssertions.

8c3c7146050db2a8.png

  1. Agrega una función de extensión a la clase NavController llamada assertCurrentRouteName() y pasa una string para el nombre de ruta esperado en la firma del método.
fun NavController.assertCurrentRouteName(expectedRouteName: String) {

}
  1. En esta función, confirma que expectedRouteName sea igual a la ruta de destino de la entrada actual de la pila de actividades del controlador de navegación.
import org.junit.Assert.assertEquals
...

fun NavController.assertCurrentRouteName(expectedRouteName: String) {
    assertEquals(expectedRouteName, currentBackStackEntry?.destination?.route)
}
  1. Abre el archivo CupcakeScreenNavigationTest y modifica la función cupcakeNavHost_verifyStartDestination() a fin de usar tu nueva función de extensión en lugar de la aserción larga.
@Test
fun cupcakeNavHost_verifyStartDestination() {
    navController.assertCurrentRouteName(CupcakeScreen.Start.name)
}

Varias pruebas también requieren interactuar con los componentes de la IU. En este codelab, esos componentes a menudo se encuentran usando una string de recursos. Puedes acceder a un elemento componible mediante su string de recursos con el método Context.getString(), sobre el que puedes leer aquí. Cuando escribes una prueba de IU en Compose, la implementación de este método se ve de la siguiente manera:

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

Esta es una instrucción detallada y se puede simplificar agregando una función de extensión.

  1. Crea un archivo nuevo en el paquete com.example.cupcake.test llamado ComposeRuleExtensions.kt. Asegúrate de que sea un archivo Kotlin simple.

82762f66f890cf1b.png

  1. Agrega el siguiente código a ese archivo.
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))

Esta función de extensión te permite reducir la cantidad de código que escribes cuando encuentras un componente de IU a través de su recurso de cadenas. En lugar de escribir esto:

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

Ahora puedes usar la siguiente instrucción:

composeTestRule.onNodeWithStringId(R.string.my_string)

Cómo verificar que la pantalla de Inicio no tenga un botón Arriba

El diseño original de la app de Cupcake no tiene un botón Up en la barra de herramientas de la pantalla de inicio.

886fb7e824608c92.png

La pantalla de inicio no tiene un botón porque no hay lugar hacia arriba desde ella, ya que se trata de la pantalla inicial. Sigue estos pasos para crear una función que confirme que la pantalla de inicio no tiene un botón Up:

  1. Crea un método llamado cupcakeNavHost_verifyBackNavigationNotShownOnStartOrderScreen() y anótalo con @Test.
@Test
fun cupcakeNavHost_verifyBackNavigationNotShownOnStartOrderScreen() {
}

En Cupcake, el botón Up tiene una descripción de contenido establecida en la cadena del recurso R.string.back_button.

  1. Crea una variable en la función de prueba con el valor del recurso R.string.back_button.
@Test
fun cupcakeNavHost_verifyBackNavigationNotShownOnStartOrderScreen() {
    val backText = composeTestRule.activity.getString(R.string.back_button)
}
  1. Confirma que no existe un nodo con esta descripción de contenido en la pantalla.
@Test
fun cupcakeNavHost_verifyBackNavigationNotShownOnStartOrderScreen() {
    val backText = composeTestRule.activity.getString(R.string.back_button)
    composeTestRule.onNodeWithContentDescription(backText).assertDoesNotExist()
}

Cómo verificar la navegación a la pantalla de sabores

Hacer clic en uno de los botones de la pantalla de inicio activa un método que le indica al controlador de navegación que debe navegar a la pantalla Flavor.

82c62c13891d627e.png

En esta prueba, escribirás un comando para hacer clic en un botón para activar esta navegación y verificar que la ruta de destino sea la pantalla Flavor.

  1. Crea una función llamada cupcakeNavHost_clickOneCupcake_navigatesToSelectFlavorScreen() y anótala con @Test.
@Test
fun cupcakeNavHost_clickOneCupcake_navigatesToSelectFlavorScreen(){
}
  1. Busca el botón One Cupcake por su ID de recurso de strings y realiza una acción de clic en él.
import com.example.cupcake.R
...

@Test
fun cupcakeNavHost_clickOneCupcake_navigatesToSelectFlavorScreen() {
    composeTestRule.onNodeWithStringId(R.string.one_cupcake)
        .performClick()
}
  1. Confirma que el nombre de la ruta actual es el nombre de la pantalla Flavor.
@Test
fun cupcakeNavHost_clickOneCupcake_navigatesToSelectFlavorScreen() {
    composeTestRule.onNodeWithStringId(R.string.one_cupcake)
        .performClick()
    navController.assertCurrentRouteName(CupcakeScreen.Flavor.name)
}

Cómo escribir más métodos de ayuda

La app de Cupcake tiene un flujo de navegación principalmente lineal. A menos que hagas clic en el botón Cancel, solo podrás navegar por la app en una dirección. Por lo tanto, a medida que pruebes pantallas en un nivel más profundo de la app, podrás repetir el código para navegar a las áreas que quieras probar. Esta situación justifica el uso de más métodos auxiliares de modo que solo tengas que escribir el código una vez.

Ahora que probaste la navegación a la pantalla Flavor, crea un método que navegue a ella, de modo que no tengas que repetir ese código para pruebas futuras.

  1. Crea un método llamado navigateToFlavorScreen().
private fun navigateToFlavorScreen() {
}
  1. Escribe un comando a fin de encontrar el botón One Cupcake y haz una acción de clic en él, como lo hiciste en la sección anterior.
private fun navigateToFlavorScreen() {
    composeTestRule.onNodeWithStringId(R.string.one_cupcake)
        .performClick()
}

Recuerda que no podrás hacer clic en el botón Next de la pantalla de sabores hasta que se seleccione un sabor. El objetivo de este método solo consiste en preparar la IU para la navegación. Después de llamarlo, la IU debería estar en un estado en el que se pueda hacer clic en el botón Next.

  1. Busca un nodo en la IU con la string R.string.chocolate y realiza una acción de clic en él para seleccionarlo.
private fun navigateToFlavorScreen() {
    composeTestRule.onNodeWithStringId(R.string.one_cupcake)
        .performClick()
    composeTestRule.onNodeWithStringId(R.string.chocolate)
        .performClick()
}

Verifica si puedes escribir métodos auxiliares que naveguen a las pantallas de retiro y resumen. Prueba este ejercicio por tu cuenta antes de ver la solución.

Usa el siguiente código para lograr esto:

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()
}

A medida que pruebes pantallas más allá de la de inicio, deberás planificar probar la funcionalidad del botón Up para asegurarte de que dirija la navegación a la pantalla anterior. Puedes crear una función auxiliar a los efectos de encontrar el botón Up y hacer clic en él.

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

Cómo maximizar la cobertura de las pruebas

El paquete de pruebas de una app debe probar la mayor cantidad de funciones posible. En un mundo perfecto, un paquete de pruebas de IU abarcaría el 100% de la funcionalidad de la IU. En la práctica, esta cobertura de la prueba es difícil de alcanzar porque existen muchos factores externos a tu app que pueden afectar la IU, como dispositivos con tamaños de pantalla únicos, diferentes versiones del sistema operativo Android y apps de terceros capaces de afectar otras apps del teléfono.

Una forma de ayudar a maximizar la cobertura de las pruebas consiste en escribir pruebas junto con las funciones a medida que las agregas. De esta manera, evitarás la necesidad de volver atrás para recordar todas las situaciones posibles cuando te adelantas con las nuevas funciones. En este punto, Cupcake es una app relativamente pequeña, y ya probaste una parte importante de su navegación. Sin embargo, hay más estados de navegación para probar.

Verifica si puedes escribir las pruebas a los efectos de comprobar los siguientes estados de navegación. Intenta implementarlas por tu cuenta antes de ver la solución.

  • Navegación a la pantalla de inicio haciendo clic en el botón Up de la pantalla de sabores
  • Navegación a la pantalla de inicio haciendo clic en el botón Cancel de la pantalla de sabores
  • Navegación a la pantalla de retiro
  • Navegación a la pantalla de sabores haciendo clic en el botón Up de la pantalla de retiro
  • Navegación a la pantalla de inicio haciendo clic en el botón Cancel de la pantalla de retiro
  • Navegación a la pantalla de resumen
  • Navegación a la pantalla de inicio haciendo clic en el botón Cancel de la pantalla de resumen
@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. Cómo escribir pruebas para la pantalla de pedidos

La navegación es solo un aspecto de la funcionalidad de la app de Cupcake. El usuario también interactúa con cada una de las pantallas de la app. Debes verificar lo que aparece en estas pantallas y que las acciones realizadas en ellas muestren los resultados correctos. El elemento SelectOptionScreen es una parte importante de la app.

En esta sección, escribirás una prueba para verificar que el contenido de esta pantalla esté configurado correctamente.

Cómo probar el contenido de la pantalla de selección de sabores

  1. Crea una nueva clase dentro del directorio app/src/androidTest llamada CupcakeOrderScreenTest, en la que se encuentran los otros archivos de prueba.

4409b8333eff0ba2.png

  1. En esta clase, crea una AndroidComposeTestRule.
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
  1. Crea una función llamada selectOptionScreen_verifyContent() y anótala con @Test.
@Test
fun selectOptionScreen_verifyContent() {

}

En esta función, configurarás el contenido de la regla de Compose como SelectOptionScreen. De esta manera, se garantiza que el elemento SelectOptionScreen componible se inicie directamente para que no se requiera navegación. Sin embargo, esta pantalla requiere dos parámetros: una lista de opciones de sabores y un subtotal.

  1. Crea una lista de opciones de sabores y un subtotal que se pasarán a la pantalla.
@Test
fun selectOptionScreen_verifyContent() {
    // Given list of options
    val flavors = listOf("Vanilla", "Chocolate", "Hazelnut", "Cookie", "Mango")
    // And subtotal
    val subtotal = "$100"
}
  1. Configura el contenido en el elemento SelectOptionScreen componible con los valores que acabas de crear.

Ten en cuenta que este enfoque es similar al inicio de un elemento componible desde la MainActivity. La única diferencia es que MainActivity llama al elemento CupcakeApp, y aquí llamas al elemento SelectOptionScreen. Poder cambiar el elemento que inicias desde setContent() te permite iniciar elementos componible específicos, en lugar de hacer que la prueba pase de forma explícita por la app para llegar al área que deseas probar. Este enfoque ayuda a evitar que la prueba falle en áreas del código que no están relacionadas con tu prueba actual.

@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)
    }
}

En este punto de la prueba, la app inicia el elemento componible SelectOptionScreen, lo que luego te permitirá interactuar con él mediante las instrucciones de la prueba.

  1. Itera en la lista de flavors y asegúrate de que cada elemento de la string aparezca en la pantalla.
  2. Usa el método onNodeWithText() a fin de buscar el texto en pantalla y el método assertIsDisplayed() para verificar que este se muestre en la app.
@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. Usa la misma técnica para comprobar que la app muestre el texto y confirma que esta muestre la string correcta del subtotal en la pantalla. Busca el ID de recurso de R.string.subtotal_price y el valor correcto del subtotal en la pantalla y, luego, confirma que la app muestra el valor.
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()
}

Recuerda que el botón Next no estará habilitado hasta que se seleccione un elemento. Esta prueba solo verifica el contenido de la pantalla, por lo que lo último que debes probar es que el botón Next esté inhabilitado.

  1. Busca el botón Next con el mismo mecanismo para encontrar un nodo por ID de recurso de cadenas. Sin embargo, en lugar de verificar que la app muestre el nodo, usa el método 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()
}

Cómo maximizar la cobertura de las pruebas

La prueba de contenido de la pantalla Choose Flavor solo prueba un aspecto de una sola pantalla. Puedes escribir varias pruebas adicionales para aumentar la cobertura de código. Intenta escribir las siguientes pruebas por tu cuenta antes de descargar el código de la solución.

  • Verifica el contenido de la pantalla de inicio.
  • Verifica el contenido de la pantalla de resumen.
  • Verifica que el botón Next esté habilitado cuando se seleccione una opción en la pantalla de selección de sabores.

Cuando escribas tus pruebas, ten en cuenta cualquier función auxiliar que pueda reducir la cantidad de código que escribas.

7. Obtén el código de la solución

Para descargar el código del codelab terminado, puedes usar este comando de git:

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

También puedes descargar el repositorio como un archivo ZIP, descomprimirlo y abrirlo en Android Studio.

Descargar ZIP

Si deseas ver el código de la solución, puedes hacerlo en GitHub.

8. Resumen

¡Felicitaciones! Aprendiste a probar el componente Navigation de Jetpack. También aprendiste algunas habilidades fundamentales para escribir pruebas de IU, como escribir métodos auxiliares reutilizables, aprovechar setContent() para escribir pruebas concisas, configurar tus pruebas con la anotación @Before y pensar en formas de lograr la máxima cobertura de prueba. A medida que continúes compilando apps para Android, recuerda seguir escribiendo pruebas junto con tu código de funciones.