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:
DeviceConfigurationOverride.DarkMode(): Anula el sistema para cambiar al tema oscuro o claro.DeviceConfigurationOverride.FontScale(): Anula la escala de la fuente del sistema.DeviceConfigurationOverride.FontWeightAdjustment(): Anula el ajuste del grosor de la fuente del sistema.DeviceConfigurationOverride.ForcedSize(): Fuerza una cantidad específica de espacio, independientemente del tamaño del dispositivo.DeviceConfigurationOverride.LayoutDirection(): Anula la dirección del diseño (de izquierda a derecha o de derecha a izquierda).DeviceConfigurationOverride.Locales(): Anula la configuración regional.DeviceConfigurationOverride.RoundScreen(): Anula si la pantalla es redonda.
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/Disableden tufailurePolicyde prueba, se anulará el argumento del ejecutor. - Si configuras un modo como
CaptureMode.Unspecified(o dejasfailurePolicycomonull), 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.