Patrones comunes

Puedes probar tu app de Compose con enfoques y patrones bien establecidos.

Prueba aislada

ComposeTestRule te permite iniciar una actividad que muestre elementos componibles: tu aplicación completa, una sola pantalla o un elemento pequeño. También es una buena práctica verificar que tus elementos de componibilidad estén encapsulados correctamente y que funcionen de forma independiente, lo que permite realizar pruebas de IU más sencillas y enfocadas.

Eso no significa que solo deberías crear pruebas de IU por unidad. También son muy importantes las pruebas de IU que abarcan partes más grandes de ella.

Cómo acceder a la actividad y a los recursos después de configurar tu propio contenido

A menudo, debes configurar el contenido que se prueba con composeTestRule.setContent y también debes acceder a los recursos de las actividades, por ejemplo, para afirmar que un texto que se muestra coincide con un recurso de cadenas. Sin embargo, no puedes llamar a setContent en una regla creada con createAndroidComposeRule() si la actividad ya la llama.

Un patrón común para lograr esto es crear un AndroidComposeTestRule con una actividad vacía, como ComponentActivity.

class MyComposeTest {

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

    @Test
    fun myTest() {
        // Start the app
        composeTestRule.setContent {
            MyAppTheme {
                MainScreen(uiState = exampleUiState, /*...*/)
            }
        }
        val continueLabel = composeTestRule.activity.getString(R.string.next)
        composeTestRule.onNodeWithText(continueLabel).performClick()
    }
}

Ten en cuenta que ComponentActivity debe agregarse al archivo AndroidManifest.xml de tu app. Para habilitar esta opción, agrega esta dependencia a tu módulo:

debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")

Propiedades semánticas personalizadas

Puedes crear propiedades de semántica personalizadas para exponer información a las pruebas. Para ello, define un SemanticsPropertyKey nuevo y haz que esté disponible con el SemanticsPropertyReceiver.

// Creates a semantics property of type Long.
val PickedDateKey = SemanticsPropertyKey<Long>("PickedDate")
var SemanticsPropertyReceiver.pickedDate by PickedDateKey

Ahora, usa esa propiedad en el modificador semantics:

val datePickerValue by remember { mutableStateOf(0L) }
MyCustomDatePicker(
    modifier = Modifier.semantics { pickedDate = datePickerValue }
)

A partir de las pruebas, usa SemanticsMatcher.expectValue para afirmar el valor de la propiedad:

composeTestRule
    .onNode(SemanticsMatcher.expectValue(PickedDateKey, 1445378400)) // 2015-10-21
    .assertExists()

Cómo verificar el restablecimiento del estado

Verifica que el estado de tus elementos de Compose se restablezca correctamente cuando se vuelva a crear la actividad o el proceso. Realiza estas verificaciones sin depender de la recreación de la actividad con la clase StateRestorationTester.

Esta clase te permite simular la recreación de un elemento que admite composición. Es especialmente útil para verificar la implementación de rememberSaveable.


class MyStateRestorationTests {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun onRecreation_stateIsRestored() {
        val restorationTester = StateRestorationTester(composeTestRule)

        restorationTester.setContent { MainScreen() }

        // TODO: Run actions that modify the state

        // Trigger a recreation
        restorationTester.emulateSavedInstanceStateRestore()

        // TODO: Verify that state has been correctly restored.
    }
}

Prueba diferentes configuraciones de dispositivos

Las apps para Android deben adaptarse a muchas condiciones cambiantes: tamaños de ventana, configuraciones regionales, tamaños de fuente, temas oscuros y claros, y mucho más. La mayoría de estas condiciones se derivan de los valores a nivel del dispositivo que controla el usuario y se exponen con la instancia Configuration actual. Probar diferentes parámetros de configuración directamente en una prueba es difícil, ya que la prueba debe configurar las propiedades a nivel del dispositivo.

DeviceConfigurationOverride es una API solo de prueba que te permite simular diferentes parámetros de configuración del dispositivo de forma localizada para el contenido de @Composable que se está probando.

El objeto complementario de DeviceConfigurationOverride tiene las siguientes funciones de extensión, que anulan las propiedades de configuración a nivel del dispositivo:

Para aplicar una anulación específica, une el contenido que se está probando en una llamada a la función de nivel superior DeviceConfigurationOverride() y pasa la anulación que se aplicará como parámetro.

Por ejemplo, el siguiente código aplica la anulación de DeviceConfigurationOverride.ForcedSize() para cambiar la densidad de forma local, lo que fuerza la renderización del elemento componible MyScreen en una ventana horizontal grande, incluso si el dispositivo en el que se ejecuta la prueba no admite ese tamaño de ventana directamente:

composeTestRule.setContent {
    DeviceConfigurationOverride(
        DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
    ) {
        MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
    }
}

Para aplicar varias anulaciones juntas, usa DeviceConfigurationOverride.then():

composeTestRule.setContent {
    DeviceConfigurationOverride(
        DeviceConfigurationOverride.FontScale(1.5f) then
            DeviceConfigurationOverride.FontWeightAdjustment(200)
    ) {
        Text(text = "text with increased scale and weight")
    }
}

Manejo personalizado de errores en las pruebas

La depuración de las pruebas de IU suele requerir la inspección del estado de la pantalla y la jerarquía de composición cuando falla una aserción.

Compose proporciona una canalización nativa de manejo de fallas para optimizar los diagnósticos. Puedes capturar automáticamente capturas de pantalla y jerarquías de IU en caso de falla, o registrar controladores personalizados para reenviar la telemetría de fallas a herramientas externas.

Configura el manejo de fallas

Para configurar el manejo de errores, pasa un objeto TestFailurePolicy a ComposeUiTestConfig.

Captura artefactos de falla integrados

Para capturar pantallas y jerarquías de la IU cuando falla la prueba, establece las propiedades screenshotCaptureMode y uiHierarchyCaptureMode en TestFailurePolicy.

class MyComposeTest {
    private val customConfig = ComposeUiTestConfig(
        failurePolicy = TestFailurePolicy(
            screenshotCaptureMode = TestFailurePolicy.CaptureMode.Enabled,
            uiHierarchyCaptureMode = TestFailurePolicy.CaptureMode.Enabled
        )
    )

    @Test
    fun myFirstTest() = runComposeUiTest(config = customConfig) {
        // ...
    }
}

Cómo usar un controlador de fallas personalizado

Para definir un comportamiento personalizado para las pruebas fallidas, agrega un TestFailureHandler a la lista failureHandlers. Cada controlador recibe un objeto FailureContext que contiene el error original y una lista de artefactos generados por los controladores de canalización anteriores.

class MyComposeTestWithHandler {
    private val customConfig = ComposeUiTestConfig(
        failurePolicy = TestFailurePolicy(
            screenshotCaptureMode = TestFailurePolicy.CaptureMode.Enabled,
            failureHandlers = listOf(
                TestFailureHandler { context ->
                    val screenshot = context.artifacts.firstOrNull {
                        it.type == FailureArtifact.Type.Screenshot
                    }
                    // ...
                }
            )
        )
    )

    @get:Rule
    val rule = createComposeRule(config = customConfig)

    // ...
}

Configura capturas para un paquete de pruebas completo

Puedes habilitar las capturas de artefactos de fallas para un paquete de pruebas completo pasando argumentos del ejecutor de instrumentación en el archivo build.gradle de tu módulo. Esto elimina la necesidad de actualizar archivos de prueba individuales.

// build.gradle.kts
android {
    defaultConfig {
        // ...
        testInstrumentationRunnerArguments["androidx.compose.ui.test.failure.isUiHierarchyCaptureEnabled"] = "true"
        testInstrumentationRunnerArguments["androidx.compose.ui.test.failure.isScreenshotCaptureEnabled"] = "true"
    }
}

Prioridad de la configuración

El framework evalúa los argumentos del ejecutor TestFailurePolicy local y a nivel del paquete:

  • Si estableces de forma explícita un modo en CaptureMode.Enabled/Disabled en tu failurePolicy de prueba, se anulará el argumento del ejecutor.
  • Si configuras un modo como CaptureMode.Unspecified (o dejas failurePolicy como null), el framework recurre al argumento del ejecutor.

Recursos adicionales

  • Cómo probar apps en Android: Es la página de destino principal de pruebas de Android, que proporciona una vista más amplia de los aspectos básicos y las técnicas de prueba.
  • Aspectos básicos de las pruebas: Obtén más información sobre los conceptos básicos detrás de las pruebas de una app para Android.
  • Pruebas locales: Puedes ejecutar algunas pruebas de forma local, en tu propia estación de trabajo.
  • Pruebas instrumentadas: También se recomienda ejecutar pruebas instrumentadas. Es decir, pruebas que se ejecutan directamente integrado en el dispositivo.
  • Integración continua: La integración continua te permite integrar tus pruebas en la canalización de implementación.
  • Prueba diferentes tamaños de pantalla: Con tantos dispositivos disponibles para los usuarios, debes realizar pruebas para diferentes tamaños de pantalla.
  • Espresso: Si bien está pensado para las IU basadas en objetos View, el conocimiento de Espresso puede ser útil para algunos aspectos de las pruebas de Compose.