Inventario de pruebas 2 - sjfuentes-uniandes/ing-sw-app-moviles GitHub Wiki
Historia de Usuario: HU004 - Consultar Información Detallada de un Artista
Tipo de Pruebas: UI/Instrumentadas con Espresso
Ubicación: app/src/androidTest/java/com/example/vinilos/HU04-detalle-artista/ArtistDetailFlowTest.kt
Framework: Android Espresso + JUnit4
Nivel: Integration/E2E (End-to-End)
Validar el flujo completo de la funcionalidad de consulta de detalle de artista desde la perspectiva del usuario, cubriendo:
- Navegación desde la lista de artistas al detalle
- Visualización correcta de la información del artista
- Retorno desde el detalle a la lista
- Comportamiento de navegación consistente entre pantallas
Descripción Funcional:
Verifica que un usuario pueda navegar desde la lista de artistas al detalle de un artista específico y regresar correctamente a la lista.
Flujo de Usuario:
- Usuario abre la aplicación
- Usuario navega al tab "Artists"
- Sistema carga y muestra la lista de artistas
- Usuario hace clic en el primer artista de la lista
- Sistema muestra la pantalla de detalle con la información del artista
- Usuario presiona el botón de regreso ("<")
- Sistema regresa a la lista de artistas
Descripción Técnica:
@Test
fun navigateToArtistDetail_and_backToList()Pasos de Ejecución:
| # | Acción | Selector | Tipo | Objetivo |
|---|---|---|---|---|
| 1 | Click en tab Artists | R.id.navigation_artists |
Bottom Navigation | Navegación a módulo |
| 2 | Espera de carga | Thread.sleep(3000) |
Delay fijo | Mitigar cold start del backend |
| 3 | Verificar lista visible | R.id.artists_recycler_view |
RecyclerView | Validar carga de datos |
| 4 | Click en primer artista |
R.id.artists_recycler_view + ViewAction personalizado |
RecyclerView Item | Navegación a detalle |
| 5 | Verificar imagen de artista | R.id.artistImage |
ImageView | Validar UI de detalle |
| 6 | Verificar nombre de artista | R.id.artistName |
TextView | Validar dato principal |
| 7 | Verificar label de descripción |
R.id.descriptionLabel con texto "Description" |
TextView | Validar estructura UI |
| 8 | Click en botón back | R.id.btnBack |
ImageButton | Acción de retorno |
| 9 | Verificar lista visible nuevamente | R.id.artists_recycler_view |
RecyclerView | Validar navegación correcta |
Assertions (Verificaciones):
- ✅ RecyclerView de artistas se muestra después de navegar al tab
- ✅ Imagen del artista es visible en el detalle
- ✅ Nombre del artista es visible en el detalle
- ✅ Label "Description" está presente con el texto correcto
- ✅ RecyclerView de artistas se muestra nuevamente tras presionar back
Cobertura Funcional:
- Navegación entre pantallas (lista ↔ detalle)
- Carga de datos desde API (con tolerancia a cold start)
- Renderizado de componentes UI del detalle
- Botón de regreso funcional
Riesgos Mitigados:
- Backend en cold start (Render): espera de 3 segundos
- Race conditions de carga asíncrona
Descripción Funcional:
Verifica que al navegar entre Home y Artists, siempre se muestre la lista de artistas (no un detalle residual del estado anterior).
Flujo de Usuario:
- Usuario abre la aplicación
- Usuario navega a Home
- Usuario navega al tab "Artists"
- Sistema muestra la lista de artistas (no un detalle guardado en memoria)
Descripción Técnica:
@Test
fun navigateHomeThenArtists_alwaysShowsArtistsList()Pasos de Ejecución:
| # | Acción | Selector | Tipo | Objetivo |
|---|---|---|---|---|
| 1 | Click en tab Home | R.id.navigation_home |
Bottom Navigation | Cambio de contexto |
| 2 | Click en tab Artists | R.id.navigation_artists |
Bottom Navigation | Navegación al módulo |
| 3 | Verificar lista visible | R.id.artists_recycler_view |
RecyclerView | Validar estado inicial correcto |
Assertions (Verificaciones):
- ✅ RecyclerView de artistas siempre se muestra al entrar al tab Artists
- ✅ No se muestra un detalle residual de una navegación anterior
Cobertura Funcional:
- Gestión correcta de back stack por MainActivity
- Limpieza de navegación entre tabs (NavOptions aplicado)
- Prevención de estado inconsistente en la navegación
Casos de Borde Validados:
- Usuario navega Artists → Detalle → Home → Artists: debe ver lista, no detalle
- Estado de navegación no persiste incorrectamente entre cambios de tab
Problema Resuelto:
Evitar dependencia de espresso-contrib que requiere sincronización compleja de Gradle y aumenta el tamaño del APK de pruebas.
Solución Implementada:
private fun clickOnRecyclerViewItem(position: Int): ViewAction {
return object : ViewAction {
override fun getConstraints(): Matcher<View> {
return isAssignableFrom(RecyclerView::class.java)
}
override fun getDescription(): String {
return "Click on RecyclerView item at position $position"
}
override fun perform(uiController: UiController, view: View) {
val recyclerView = view as RecyclerView
val viewHolder = recyclerView.findViewHolderForAdapterPosition(position)
viewHolder?.itemView?.performClick()
}
}
}Características:
- ✅ Accede directamente al ViewHolder por posición
- ✅ Ejecuta
performClick()en la vista del item - ✅ No requiere bibliotecas externas adicionales
- ✅ Más ligero que
RecyclerViewActions.actionOnItemAtPosition()
Limitaciones:
- Solo funciona si el ViewHolder ya está vinculado (visible en pantalla)
- No hace scroll automático; asume que el item en posición 0 es visible
androidTestImplementation(libs.androidx.junit) // JUnit para Android
androidTestImplementation(libs.androidx.espresso.core) // Espresso Core
androidTestImplementation("androidx.test:runner:1.6.2") // Test Runner
androidTestImplementation("androidx.test:rules:1.6.0") // Activity Test RulesNota: No se requiere espresso-contrib gracias al ViewAction personalizado.
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"-
@LargeTest: Indica que son pruebas de integración (E2E), con tiempo de ejecución mayor -
@RunWith(AndroidJUnit4::class): Ejecutor de pruebas para Android -
@Rule: Regla de JUnit para lanzar la actividad principal -
@Test: Marca los métodos como casos de prueba
-
Ejecutar todos los tests de HU04:
- Navegar a
ArtistDetailFlowTest.kt - Click derecho en el nombre de la clase
- Seleccionar "Run 'ArtistDetailFlowTest'"
- Navegar a
-
Ejecutar un test individual:
- Click en el ícono
▶️ junto al método@Test - Seleccionar "Run 'navigateToArtistDetail_and_backToList()'"
- Click en el ícono
# Ejecutar todos los tests instrumentados
./gradlew connectedAndroidTest
# Ejecutar solo los tests de HU04
./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.example.vinilos.HU04_detalle_artista.ArtistDetailFlowTest- ✅ Emulador o dispositivo físico conectado
- ✅ API level 24 o superior (minSdk configurado en el proyecto)
- ✅ Conexión a Internet (para acceder al backend de la API)
⚠️ Backend disponible en:https://ing-sw-api-mobile.onrender.com/bands
Tiempo estimado de ejecución:
- Primer test: ~6-8 segundos (incluye cold start del backend)
- Segundo test: ~2-3 segundos
| Escenario | Estado | Test |
|---|---|---|
| Navegación a lista de artistas | ✅ Cubierto | ambos tests |
| Click en un artista de la lista | ✅ Cubierto | Test 1 |
| Visualización de detalle completo | ✅ Cubierto | Test 1 |
| Botón de regreso desde detalle | ✅ Cubierto | Test 1 |
| Retorno a lista tras back | ✅ Cubierto | Test 1 |
| Navegación Home → Artists → Lista | ✅ Cubierto | Test 2 |
| Estado de navegación limpio | ✅ Cubierto | Test 2 |
Problema:
El backend alojado en Render puede tardar hasta 30 segundos en despertar si no ha recibido tráfico reciente.
Mitigación Actual:
Thread.sleep(3000) // Espera de 3 segundosMejora Propuesta:
Implementar IdlingResource que espere hasta que el RecyclerView tenga al menos 1 item:
// Pseudo-código (no implementado)
class RecyclerViewIdlingResource(private val recyclerView: RecyclerView) : IdlingResource {
override fun isIdleNow(): Boolean {
return recyclerView.adapter?.itemCount ?: 0 > 0
}
}Problema:
Si el item en posición 0 no está visible (por scroll previo), findViewHolderForAdapterPosition() retorna null.
Manejo Actual:
viewHolder?.itemView?.performClick() // Safe call, no falla si es nullLimitación:
El test pasaría silenciosamente sin hacer clic.
Mejora Propuesta:
val viewHolder = recyclerView.findViewHolderForAdapterPosition(position)
?: throw PerformException.Builder()
.withActionDescription(this.description)
.withViewDescription(HumanReadables.describe(view))
.withCause(IllegalStateException("ViewHolder at position $position not found"))
.build()
viewHolder.itemView.performClick()Riesgo:
Si se renombran IDs en los layouts XML, los tests fallan con NoMatchingViewException.
Prevención:
- Mantener IDs estables durante el desarrollo
- Usar constantes centralizadas si hay múltiples referencias
Detección Temprana:
- Los tests fallan inmediatamente en CI/CD
- Error claro:
No views in hierarchy found matching: with id: R.id.artists_recycler_view
| Métrica | Valor Esperado | Valor Actual |
|---|---|---|
| Tests pasando | 2/2 (100%) | ✅ 2/2 |
| Tiempo de ejecución | < 15 segundos | ~8-10 seg |
| Cobertura de flujo principal | 100% | ✅ 100% |
| False positives | 0 | ✅ 0 |
| Flakiness (inestabilidad) | < 5% |
-
Backend no disponible:
- Error:
NetworkExceptiono timeout - Solución: Verificar conexión a Internet y disponibilidad del endpoint
- Error:
-
Cambios en la UI:
- Error:
NoMatchingViewException - Solución: Actualizar IDs en los tests tras refactorización de layouts
- Error:
-
Datos del backend inconsistentes:
- Error: Lista vacía o artista sin imagen
- Solución: Usar mock del backend o validar datos de prueba
- ✅ Al cambiar IDs de vistas en layouts
- ✅ Al modificar el flujo de navegación (ej: agregar pantalla intermedia)
- ✅ Al cambiar strings (ej: "Description" → "Descripción")
- ✅ Al agregar nuevos campos en el detalle del artista
Estos tests están vinculados a:
- HU004: Consultar Detalle de Artista
- Sprint: 2 (ejemplo)
- Versión de API: v1 (backend)
Historia de Usuario: HU07 - Crear álbum
Tipo de Pruebas: UI/Instrumentadas con Espresso
Ubicación: app/src/androidTest/java/com/example/vinilos/HU07-crear-album/AddAlbumFragmentTest.kt
Framework: Android Espresso + JUnit4
Nivel: Integration/E2E (End-to-End)
Validar el flujo completo de la creación de un álbum.
- Navegación desde el home a lista de álbumes
- Navegación desde lista de álbumes a formulario de creación
- Ingreso de datos en formulario y validaciones
- Selección de artista, imagen y tracks
- Creación de un álbum y navegación de regreso
Descripción Funcional:
Verifica que un usuario pueda navegar desde el home hasta el formulario de creación de un álbum, completar todos los campos requeridos, seleccionar un artista, agregar una imagen, agregar tracks, y crear exitosamente un álbum que regrese al listado.
Flujo de Usuario:
- Usuario abre la aplicación
- Usuario hace click en "Albums" (botón de navegación)
- Sistema carga y muestra la lista de álbumes
- Usuario hace clic en "Crear álbum"
- Sistema muestra el formulario de crear álbum con título "Crear Album"
- Usuario llena información requerida:
- Nombre del álbum
- Descripción
- Fecha de lanzamiento
- Selecciona un artista de la lista
- Género
- Sello discográfico
- Selecciona una imagen
- Agrega un track
- Usuario hace clic en "Crear álbum"
- Sistema envía información y regresa a listado de álbumes
Descripción Técnica:
@Test
fun createAlbum_fillsFormAndSubmits_navigatesBack()Pasos de Ejecución:
| # | Acción | Selector | Tipo | Objetivo |
|---|---|---|---|---|
| 1 | Verificar botón Albums visible | R.id.btn_albums |
Button | Validar UI inicial |
| 2 | Click en botón Albums | R.id.btn_albums |
Button | Navegación a módulo de álbumes |
| 3 | Espera de carga | Thread.sleep(2000) |
Delay fijo | Mitigar cold start del backend |
| 4 | Verificar botón crear álbum visible | R.id.add_album_button |
Button | Validar carga de lista |
| 5 | Click en crear álbum | R.id.add_album_button |
Button | Navegación a formulario |
| 6 | Espera de carga | Thread.sleep(2000) |
Delay fijo | Esperar renderizado del formulario |
| 7 | Verificar título del formulario | withText("Crear Album") |
TextView | Validar pantalla correcta |
| 8 | Verificar campo nombre visible | R.id.name_edit_text |
EditText | Validar UI del formulario |
| 9 | Click y llenar nombre | R.id.name_edit_text |
EditText | Ingresar "Nuevo Álbum de Prueba" |
| 10 | Verificar campo descripción visible | R.id.description_edit_text |
EditText | Validar UI del formulario |
| 11 | Click y llenar descripción | R.id.description_edit_text |
EditText | Ingresar descripción de prueba |
| 12 | Verificar campo fecha visible | R.id.release_date_edit_text |
EditText | Validar UI del formulario |
| 13 | Llenar fecha de lanzamiento | R.id.release_date_edit_text |
EditText | Ingresar "2024-01-01" |
| 14 | Seleccionar artista | Acceso directo al fragment | Programático | Seleccionar primer artista disponible |
| 15 | Verificar campo género visible | R.id.genre_edit_text |
EditText | Validar UI del formulario |
| 16 | Click y llenar género | R.id.genre_edit_text |
EditText | Ingresar "Rock" |
| 17 | Verificar campo sello visible | R.id.record_label_edit_text |
EditText | Validar UI del formulario |
| 18 | Click y llenar sello discográfico | R.id.record_label_edit_text |
EditText | Ingresar "Sony Music" |
| 19 | Mockear selección de imagen | Intent.ACTION_GET_CONTENT |
Intent Mock | Simular selección de imagen |
| 20 | Click en botón buscar imagen | R.id.browse_image_button |
Button | Abrir selector de imagen |
| 21 | Agregar track | Acceso directo al fragment | Programático | Agregar "Demo Track A" |
| 22 | Scroll y verificar botón crear | R.id.create_album_button |
Button | Validar botón visible |
| 23 | Click en crear álbum | R.id.create_album_button |
Button | Enviar formulario |
| 24 | Espera de procesamiento | Thread.sleep(1500) |
Delay fijo | Esperar respuesta del servidor |
| 25 | Verificar regreso a lista | R.id.add_album_button |
Button | Validar navegación de regreso |
Assertions (Verificaciones):
- ✅ Botón de Albums se muestra en la pantalla inicial
- ✅ Botón de crear álbum se muestra después de navegar al módulo
- ✅ Título "Crear Album" es visible en el formulario
- ✅ Todos los campos del formulario son visibles y editables:
- Campo nombre
- Campo descripción
- Campo fecha de lanzamiento
- Campo género
- Campo sello discográfico
- ✅ Artista se selecciona correctamente desde la lista disponible
- ✅ Imagen se selecciona mediante mock de Intent
- ✅ Track se agrega correctamente a la lista
- ✅ Botón de crear álbum es visible y clickeable
- ✅ Después de crear, se regresa al listado de álbumes (botón crear visible nuevamente)
Cobertura Funcional:
- Navegación entre pantallas (home → lista → formulario → lista)
- Carga de datos desde API (artistas disponibles)
- Renderizado de componentes UI del formulario
- Validación de campos requeridos
- Selección de artista desde lista desplegable
- Selección de imagen mediante Intent
- Agregado de tracks dinámicamente
- Envío de formulario y creación de álbum
- Navegación de regreso tras creación exitosa
Riesgos Mitigados:
- Backend en cold start (Render): esperas de 2 segundos en puntos críticos
- Race conditions de carga asíncrona de artistas
- Selección de imagen requiere mock de Intent (no se puede abrir selector real en tests)
- Interacciones programáticas necesarias para campos complejos (artista, tracks)
Notas Técnicas:
- Uso de
forceClick()personalizado para asegurar clicks en elementos que pueden no estar completamente interactuables - Acceso directo al fragment mediante
activityRule.scenario.onActivitypara operaciones complejas (selección de artista, agregado de tracks) - Mock de Intent para simular selección de imagen sin abrir el selector real del sistema
- Uso de
Intents.init()yIntents.release()para manejo de Intents mockeados - Cierre de teclado virtual (
closeSoftKeyboard()) para evitar interferencias en las interacciones
Historia de Usuario: HU002 - Consultar Información Detallada de un Álbum Tipo de Pruebas: UI/Instrumentadas con Espresso Ubicación: app/src/androidTest/java/com/example/vinilos/HU02-detalle-album/AlbumDetailFlowTest.kt Framework: Android Espresso + JUnit4 Nivel: Integration/E2E (End-to-End)
Validar el flujo completo de la funcionalidad de consulta de detalle de álbum desde la perspectiva del usuario, cubriendo:
- Navegación desde la lista de álbumes al detalle.
- Visualización correcta de la información del álbum.
- Retorno desde el detalle a la lista.
- Comportamiento de navegación consistente entre pantallas.
Descripción Funcional: Verifica que un usuario pueda navegar desde la lista de álbumes al detalle de un álbum específico y regresar correctamente a la lista.
Flujo de Usuario: 1.Usuario abre la aplicación. 2.Usuario navega al tab "Albums". 3.Sistema carga y muestra la lista de álbumes. 4.Usuario hace clic en el primer álbum de la lista. 5.Sistema muestra la pantalla de detalle con la información del álbum. 6.Usuario presiona el botón de regreso ("<"). 7.Sistema regresa a la lista de álbumes.
Descripción Técnica:
@Test
fun navigateToAlbumDetail_and_backToList()Pasos de Ejecución:
| # | Acción | Selector | Tipo | Objetivo |
|---|---|---|---|---|
| 1 | Click en tab Albums | R.id.navigation_albums | Bottom Navigation | Navegación a módulo |
| 2 | Espera de carga | Thread.sleep(3000) | Delay fijo | Mitigar cold start del backend |
| 4 | Click en primer álbum | R.id.albumsRv + ViewAction personalizado | RecyclerView Item | Navegación a detalle |
| 5 | Verificar carátula de álbum | R.id.albumCover | ImageView | Validar UI de detalle |
| 6 | Verificar nombre de álbum | R.id.albumName | TextView | Validar dato principal |
| 7 | Verificar label de descripción | R.id.descriptionLabel con texto R.string.description | TextView | Validar estructura UI |
| 8 | Click en botón back | R.id.btnBack | ImageButton | Acción de retorno |
| 9 | Verificar lista visible nuevamente | R.id.albumsRv | RecyclerView | Validar navegación correcta |
Assertions (Verificaciones): •✅ RecyclerView de álbumes se muestra después de navegar al tab •✅ Carátula del álbum es visible en el detalle •✅ Nombre del álbum es visible en el detalle •✅ Label "Description" está presente con el texto correcto •✅ RecyclerView de álbumes se muestra nuevamente tras presionar back
Cobertura Funcional: •Navegación entre pantallas (lista ↔ detalle) •Carga de datos desde API (con tolerancia a cold start) •Renderizado de componentes UI del detalle •Botón de regreso funcional
Riesgos Mitigados: •Backend en cold start (Render): espera de 3 segundos •Race conditions de carga asíncrona