1. Antes de comenzar
En codelabs anteriores, aprendiste a usar elementos ViewModel
a fin de controlar la lógica empresarial, además de LiveData
para IU reactivas. En este codelab, aprenderás a escribir pruebas de unidades con el propósito de verificar que el código ViewModel
funcione correctamente.
Requisitos previos
- Haber creado directorios de prueba en Android Studio
- Haber escrito pruebas de instrumentación y de unidades en Android Studio
- Haber agregado dependencias de Gradle a un proyecto de Android
Qué aprenderás
- Cómo escribir pruebas de unidades para elementos
ViewModel
yLiveData
Requisitos
- Una computadora que tenga Android Studio instalado
- El código de la solución de la app de Cupcake
Descarga el código de partida para este codelab
En este codelab, agregarás pruebas de instrumentación a la app de Cupcake a partir de códigos de solución anteriores.
Para obtener el código necesario para este codelab y abrirlo en Android Studio, haz lo siguiente:
Obtén el código
- Haz clic en la URL proporcionada. Se abrirá la página de GitHub del proyecto en un navegador.
- Verifica y confirma que el nombre de la rama coincida con el nombre de la rama que se especifica en el codelab. Por ejemplo, en la siguiente captura de pantalla, el nombre de la rama es main.
- En esa página, haz clic en el botón Code, que abre una ventana emergente.
- En la ventana emergente, haz clic en el botón Download ZIP para guardar el proyecto en tu computadora. Espera a que se complete la descarga.
- Ubica el archivo en tu computadora (probablemente en la carpeta Descargas).
- Haz doble clic en el archivo ZIP para descomprimirlo. Se creará una carpeta nueva con los archivos del proyecto.
Abre el proyecto en Android Studio
- Inicia Android Studio.
- En la ventana Welcome to Android Studio, haz clic en Open.
Nota: Si Android Studio ya está abierto, selecciona la opción de menú File > Open.
- En el navegador de archivos, ve hasta donde se encuentra la carpeta de proyecto descomprimido (probablemente en Descargas).
- Haz doble clic en la carpeta del proyecto.
- Espera a que Android Studio abra el proyecto.
- Haz clic en el botón Run para compilar y ejecutar la app. Asegúrate de que funcione como se espera.
2. Descripción general de la app de partida
La app de Cupcake consiste de una pantalla principal que muestra una pantalla de pedidos con tres opciones para las cantidades de cupcakes. Si haces clic en una opción, irás a una pantalla en la que podrás seleccionar un sabor y, luego, navegarás a otra en la que seleccionarás una fecha de retiro del pedido. Luego podrás enviar el pedido a otra app y, además, podrás cancelarlo en cualquiera de estas etapas.
3. Crea los directorios de prueba de unidades
Crea un directorio de prueba de unidades para la app de Cupcake como lo hiciste en codelabs anteriores.
4. Crea una clase de prueba de unidades
Crea una clase nueva llamada ViewModelTests.kt.
5. Agrega las dependencias necesarias
Agrega las siguientes dependencias a tu proyecto:
testImplementation 'junit:junit:4.+'
testImplementation 'androidx.arch.core:core-testing:2.1.0'
Ahora sincroniza tu proyecto.
6. Escribe una prueba de ViewModel
Comencemos con una prueba simple. Lo primero que haremos cuando interactuamos con la app en un dispositivo o emulador será seleccionar la cantidad de cupcakes. Por lo tanto, primero probaremos el método setQuantity()
en el OrderViewModel
y verificaremos el valor del objeto quantity
LiveData
.
La variable quantity
, que probaremos, es una instancia de LiveData
. Probar objetos LiveData
requiere un paso adicional, que es donde entra en juego la dependencia que agregamos. Usaremos LiveData
para actualizar la IU en cuanto cambie un valor. Nuestra IU se ejecutará en lo que llamamos el "subproceso principal". Si desconoces los subprocesos y la simultaneidad, no te preocupes: los analizaremos en profundidad en otros codelabs. Por ahora, en el contexto de una app para Android, piensa en el subproceso principal como el de IU. El código que muestra la IU a un usuario se ejecuta en este subproceso. A menos que se especifique lo contrario, en una prueba de unidades se supone que todo se ejecuta en el subproceso principal. Sin embargo, debido a que los objetos LiveData
no pueden acceder al subproceso principal, debemos indicar de forma explícita que estos objetos LiveData
no deben llamar a dicho subproceso.
- Para especificar que los objetos
LiveData
no deben llamar al subproceso principal, debemos proporcionar una regla de prueba específica cada vez que probemos un objetoLiveData
.
@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
- Ahora podemos crear una función llamada
quantity_twelve_cupcakes()
. En el método, crea una instancia deOrderViewModel.
. - En esta prueba, verificarás que el objeto
quantity
enOrderViewModel
esté actualizado cuando se llame asetQuantity
. Sin embargo, antes de llamar a cualquier método o trabajar con datos enOrderViewModel
, es importante tener en cuenta que, cuando se prueban los valores de un objetoLiveData
, se deben observar los objetos para que se emitan los cambios. Una manera sencilla de hacerlo es usar el métodoobserveForever
. Llama al métodoobserveForever
en el objetoquantity
. Este método requiere una expresión lambda, pero se puede dejar en blanco. - Luego, llama al método
setQuantity()
y pasa12
como parámetro.
val viewModel = OrderViewModel()
viewModel.quantity.observeForever {}
viewModel.setQuantity(12)
- Podemos inferir de forma segura que el valor del objeto
quantity
es12
. Ten en cuenta que los objetosLiveData
no son el valor propiamente dicho. Los valores se encuentran en una propiedad llamadavalue
. Realiza la siguiente aserción:
assertEquals(12, viewModel.quantity.value)
La prueba debería verse de la siguiente manera:
@Test
fun quantity_twelve_cupcakes() {
val viewModel = OrderViewModel()
viewModel.quantity.observeForever {}
viewModel.setQuantity(12)
assertEquals(12, viewModel.quantity.value)
}
Ejecuta la prueba. ¡Felicitaciones! Acabas de escribir tu primera prueba de unidades de LiveData
, que es una habilidad crítica en Modern Android Development. Con ella no se prueba mucha lógica empresarial, así que escribamos una un poco más compleja.
Una de las funciones principales de OrderViewModel
es calcular el precio de nuestro pedido. Esto sucede cuando seleccionamos una cantidad de cupcakes y una fecha de retiro. El cálculo del precio se realiza en un método privado, por lo que nuestra prueba no puede llamar directamente a este método. Solo otros métodos en OrderViewModel
pueden hacerlo. Esos métodos son públicos, por lo que los llamaremos para activar el cálculo del precio de modo que podamos verificar que el valor sea el esperado.
Prácticas recomendadas
El precio se actualiza cuando se selecciona la cantidad de cupcakes y la fecha. Si bien deberían probarse ambas opciones, suele ser preferible realizar pruebas para una sola funcionalidad. Por lo tanto, crearemos métodos separados para cada prueba: una para probar el precio cuando se actualice la cantidad y otra para probarlo cuando se actualice la fecha. No queremos que falle el resultado de una prueba a causa de una prueba fallida diferente.
- Crea un método llamado
price_twelve_cupcakes()
y anótalo como una prueba. - En él, crea una instancia del
OrderViewModel
, llama al métodosetQuantity()
y pasa 12 como un parámetro.
val viewModel = OrderViewModel()
viewModel.setQuantity(12)
- Si observas el
PRICE_PER_CUPCAKE
enOrderViewModel
, podemos ver que los cupcakes tienen un valor de USD 2.00 cada uno. También podemos ver que se llama aresetOrder()
cada vez que se inicializaViewModel
; en este método, la fecha predeterminada es la de hoy y elPRICE_FOR_SAME_DAY_PICKUP
es USD 3.00. Por lo tanto, 12 * 2 + 3 = 27. Se espera que el valor de la variableprice
, después de seleccionar 12 cupcakes, sea de USD 27.00. Declaremos que nuestro valor esperado de USD 27.00 equivale al valor del objetoprice LiveData
.
assertEquals("$27.00", viewModel.price.value)
Ahora, ejecuta la prueba.
Debería fallar.
El resultado de la prueba indica que nuestro valor real era null
. Hay una explicación para esto. Si observas la variable price
en OrderViewModel
, verás lo siguiente:
val price: LiveData<String> = Transformations.map(_price) {
// Format the price into the local currency and return this as LiveData<String>
NumberFormat.getCurrencyInstance().format(it)
}
Este es un ejemplo de los motivos por el que LiveData
debería observarse durante las pruebas. El valor de price
se establece mediante Transformation
. En esencia, este código toma el valor que asignamos a price
y lo transforma a un formato de moneda de modo que no tengamos que hacerlo de forma manual. Sin embargo, este código tiene otras implicaciones. Cuando se transforma un objeto LiveData
, no se llama al código a menos que sea absolutamente necesario; esto ahorra recursos en un dispositivo móvil. Solo se llamará al código si observamos el objeto en busca de cambios. Desde luego, esto se hace en nuestra app, pero debemos hacer lo mismo para la prueba también.
- En el método de prueba, agrega la siguiente línea antes de configurar la cantidad:
viewModel.price.observeForever {}
La prueba debería verse de la siguiente manera:
@Test
fun price_twelve_cupcakes() {
val viewModel = OrderViewModel()
viewModel.price.observeForever {}
viewModel.setQuantity(12)
assertEquals("$27.00", viewModel.price.value)
}
Ahora, si ejecutas la prueba, debería ser exitosa.
7. Código de solución
8. Felicitaciones
En este codelab, aprendimos a hacer lo siguiente:
- Configurar una prueba
LiveData
- Probar
LiveData
- Probar
LiveData
que se transformó - Observar
LiveData
en una prueba de unidades