Configuración del Proyecto
Clase 3 de 16 • Curso de Android Testing
Resumen
La implementación de pruebas en aplicaciones Android es fundamental para garantizar la calidad y estabilidad del software. Dominar las técnicas de testing te permitirá desarrollar aplicaciones más robustas y detectar errores antes de que lleguen a producción. En este recorrido, exploraremos un proyecto estructurado que servirá como base para aplicar diferentes estrategias de testing en Android.
¿Cómo está estructurado el proyecto de testing?
El proyecto sobre el cual trabajaremos sigue una arquitectura por capas, similar a la que encontrarías en un entorno profesional. Esta estructura facilita la aplicación de diferentes tipos de pruebas según la capa que queramos evaluar.
La organización principal del proyecto incluye tres carpetas fundamentales:
- Data: Contiene la implementación de acceso a datos y APIs
- Domain: Alberga las clases de modelo y contratos de repositorios
- Presentation: Incluye los componentes de UI y ViewModels
Explorando la capa de datos
En la capa de datos encontramos una implementación que simula llamadas a una API externa mediante Retrofit. La interfaz UserAPI
define tres endpoints principales:
// Ejemplo similar a la interfaz UserAPI
interface UserAPI {
fun getUser(userId: String): User
fun getPlaces(userId: String): List<Place>
fun getProfile(userId: String): Profile
}
La implementación del repositorio (UserRepositoryImplementation
) muestra cómo se manejan estas llamadas utilizando coroutines y builders para gestionar los resultados. Este componente es crucial ya que:
- Implementa llamadas asíncronas usando coroutines
- Maneja errores y resultados exitosos
- Combina múltiples llamadas para construir objetos complejos
Por ejemplo, el método fetchUser
obtiene información del usuario, mientras que getPlaces
recupera los lugares asociados a ese usuario. Estos datos se combinan para construir un perfil completo.
Entendiendo la capa de dominio
La capa de dominio contiene las clases de datos que representan nuestro modelo de negocio:
// Ejemplos de clases de dominio
data class Place(val id: String, val name: String, val coordinates: Coordinates)
data class Coordinates(val latitude: Double, val longitude: Double)
data class User(val id: String, val userName: String)
data class Profile(val user: User, val places: List<Place>)
También encontramos la interfaz UserRepository
que define el contrato que debe implementar cualquier repositorio de usuarios, siguiendo el principio de inversión de dependencias.
Analizando la capa de presentación
En la capa de presentación tenemos:
- ProfileViewModel: Recibe un ID de usuario y actualiza el estado de la UI según los resultados obtenidos del repositorio
- ProfileScreen: Un composable que muestra diferentes estados (carga, error o contenido) según la información recibida del ViewModel
¿Qué tipos de pruebas implementaremos?
El proyecto está configurado para soportar diferentes tipos de pruebas, siguiendo la pirámide de testing:
Pruebas unitarias
Las pruebas unitarias se almacenan en la carpeta src/test
. Estas pruebas evalúan componentes individuales de forma aislada y son fundamentales para verificar la lógica de negocio.
Pruebas de instrumentación
Las pruebas de instrumentación (o pruebas de integración) se ubican en la carpeta src/androidTest
. Estas pruebas requieren un dispositivo o emulador Android y evalúan la interacción entre componentes o con el sistema Android.
¿Qué librerías utilizaremos para testing?
El proyecto incorpora varias librerías especializadas para testing:
- JUnit: Framework base para pruebas unitarias
- Compose UI Testing: Librerías para probar composables de Jetpack Compose
- Mockk: Librería para crear mocks en Kotlin, esencial para simular dependencias
- MockWebServer: Permite simular respuestas de servidores para probar llamadas de red
- Kotlin Coroutines Test: Proporciona utilidades para probar código con coroutines
- Turbine: Facilita las pruebas de Kotlin Flows
- Truth: Framework de aserciones con sintaxis más legible y mensajes de error más claros
La configuración del archivo build.gradle
asegura que estas dependencias estén disponibles en los contextos adecuados:
// Dependencias para pruebas unitarias
testImplementation "junit:junit:$junit_version"
testImplementation "com.google.truth:truth:$truth_version"
testImplementation "io.mockk:mockk:$mockk_version"
testImplementation "com.squareup.okhttp3:mockwebserver:$mockwebserver_version"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
testImplementation "app.cash.turbine:turbine:$turbine_version"
// Dependencias para pruebas de instrumentación
androidTestImplementation "androidx.test.ext:junit:$androidx_junit_version"
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
Es importante destacar la configuración packagingOptions
en el bloque android
del archivo build.gradle
, que es necesaria para resolver conflictos de archivos durante la ejecución de pruebas.
¿Cómo comenzar con las pruebas unitarias?
Para implementar pruebas efectivas, es fundamental comprender la estructura del proyecto y las responsabilidades de cada componente. En las próximas secciones, comenzaremos a escribir pruebas unitarias para verificar el comportamiento de nuestros componentes de forma aislada.
Cada archivo del proyecto tiene un propósito específico que iremos descubriendo a medida que implementemos diferentes estrategias de testing. La combinación de estas técnicas nos permitirá construir una suite de pruebas robusta que garantice la calidad de nuestra aplicación.
Te invito a explorar más sobre las librerías mencionadas y compartir en los comentarios cualquier dato interesante que encuentres sobre ellas. En la próxima sección, nos sumergiremos en la implementación de nuestra primera prueba unitaria.