Cuando se diseña la estrategia de prueba de un elemento o sistema, hay tres aspectos de prueba relacionados:
- Alcance: ¿Qué parte del código abarca la prueba? Las pruebas pueden verificar un solo de una aplicación, toda la aplicación o algún punto intermedio. El el alcance probado está en prueba y comúnmente se refiere a él como el Sujeto Prueba, también el Sistema bajo prueba o la Unidad en prueba.
- Velocidad: ¿Qué tan rápido se ejecuta la prueba? Las velocidades de prueba pueden variar de milisegundos a varios minutos.
- Fidelidad: Cómo se ve en el "mundo real" es la prueba? Por ejemplo, si parte del código que estás probando, necesita hacer una solicitud de red, ¿el código de prueba esta solicitud de red o falsifica el resultado? Si la prueba realmente habla con la red, esto significa que tiene mayor fidelidad. La desventaja es que el la prueba podría tardar más en ejecutarse, podría generar errores si la red no funciona o podría ser costoso.
Consulta qué probar para obtener información sobre cómo comenzar a definir tu estrategia de prueba.
Aislamiento y dependencias
Cuando pruebas un elemento o sistema de elementos, lo haces en aislamiento. Para Por ejemplo, para probar un ViewModel, no necesitas iniciar un emulador e iniciar una IU. porque no depende (o no debería) del framework de Android.
Sin embargo, es posible que el sujeto de prueba dependa de otros para que funcione. Para por ejemplo, un ViewModel podría depender de un repositorio de datos para funcionar.
Cuando necesitas proporcionarle una dependencia a un sujeto de prueba, una práctica común es crear un doble de prueba (o, también, un objeto de prueba). Los dobles de prueba son objetos que verse y actuar como componentes en tu aplicación, pero se crean en tu prueba para proporcionan un comportamiento o datos específicos. La ventaja principal es que realizar pruebas de forma más rápida y sencilla.
Tipos de dobles de prueba
Existen varios tipos de dobles de prueba:
Falso | Un doble de prueba que tiene la palabra "funciona" implementación de la clase, pero que se implementa de manera tal que sea buena para las pruebas, pero no adecuada para producción.
Ejemplo: Una base de datos en la memoria Las falsificaciones no requieren un framework de simulación y son livianas. Son preferidas. |
---|---|
Simulación | Es un doble de prueba que comporta la forma en que lo programas para que se comporte y tiene expectativas sobre sus interacciones. Las simulaciones fallarán en las pruebas si sus interacciones no coinciden con los requisitos que definiste. Por lo general, las simulaciones se crean con un framework de simulación para lograr todo esto.
Ejemplo: Verifica que se llamó a un método en una base de datos exactamente una vez. |
Stub | Es un doble de prueba que comporta la forma en la que lo programas para que se comporte, pero no tiene expectativas sobre sus interacciones. Por lo general, se crea con un framework de simulación. Por cuestiones de simplicidad, se prefieren las falsificaciones en lugar de los stubs. |
Cuenta ficticia | Es un doble de prueba que se pasa, pero no se usa, por ejemplo, si solo necesitas proporcionarlo como parámetro.
Ejemplo: Una función vacía pasada como una devolución de llamada de clic |
Espía | Wrapper sobre un objeto real que también realiza un seguimiento de información adicional, similar a las simulaciones. Por lo general, se evitan porque agregan complejidad. Por lo tanto, se prefieren las falsificaciones o las simulaciones en lugar de los espías. |
Sombra | Uso falso en Robolectric. |
Ejemplo en el que se usa
Supongamos que quieres realizar una prueba de unidades de un ViewModel que depende de una interfaz.
llamado UserRepository
y expone el nombre del primer usuario en una IU. Puedes
crea un doble de prueba falso implementando la interfaz y mostrando
de datos no estructurados.
object FakeUserRepository : UserRepository {
fun getUsers() = listOf(UserAlice, UserBob)
}
val const UserAlice = User("Alice")
val const UserBob = User("Bob")
Este UserRepository
falso no necesita depender de los datos locales y remotos
de las fuentes que usaría la versión de producción. El archivo se encuentra en la fuente de prueba.
y no se enviará con la app de producción.
La siguiente prueba verifica que ViewModel exponga correctamente al primer usuario nombre a la vista.
@Test
fun viewModelA_loadsUsers_showsFirstUser() {
// Given a VM using fake data
val viewModel = ViewModelA(FakeUserRepository) // Kicks off data load on init
// Verify that the exposed data is correct
assertEquals(viewModel.firstUserName, UserAlice.name)
}
Reemplazar UserRepository
por una instancia falsa es fácil en una prueba de unidades, porque el
El verificador crea ViewModel. Sin embargo, puede ser difícil reemplazar
elementos arbitrarios en pruebas más grandes.
Reemplazo de componentes y la inserción de dependencias
Cuando las pruebas no tienen control sobre la creación de los sistemas a prueba, reemplazar componentes de pruebas dobles es más complicado y requiere de tu app para que siga un diseño que se pueda probar.
Incluso las pruebas grandes de extremo a extremo pueden beneficiarse del uso de dobles de prueba, como de IU instrumentada que navega por un flujo de usuarios completo en tu app. En En ese caso, recomendamos que la prueba sea hermética. Una prueba hermética evita todas las dependencias externas, como la recuperación de datos de Internet. Esta mejora la confiabilidad y el rendimiento.
Puedes diseñar tu app para lograr esta flexibilidad de forma manual, pero te recomendamos con un framework de inserción de dependencias, como Hilt, para reemplazar componentes. en tu app en el momento de la prueba. Consulta la guía de prueba de Hilt.
Robolectric
En Android, puedes usar el framework de Robolectric, que proporciona un tipo especial de doble de prueba. Robolectric te permite ejecutar las pruebas en en tu estación de trabajo o en un entorno de integración continua. Utiliza un JVM normal, sin un emulador ni dispositivo. Simula el aumento de las vistas, la carga de recursos y otras partes del framework de Android con dobles de prueba llamadas sombras.
Robolectric es un simulador, por lo que no debe reemplazar las pruebas de unidades simples ni usarse para realizar pruebas de compatibilidad. Proporciona velocidad y reduce los costos a expensas de menor fidelidad en algunos casos. Un buen enfoque para las pruebas de IU es hacerlas compatibles con las pruebas de Robolectric y las instrumentadas, y decide cuándo se en función de la necesidad de probar su funcionalidad o compatibilidad. Ambos Espresso y Compose se pueden ejecutar en Robolectric.