Cómo crear pruebas de unidades con la biblioteca de pruebas de Health Connect

La biblioteca de pruebas de Health Connect (androidx.health.connect:connect-testing) simplifica la creación de pruebas automatizadas. Puedes usar esta biblioteca para verificar el comportamiento de tu aplicación y validar que responda correctamente a casos poco comunes, que son difíciles de probar manualmente.

Puedes usar la biblioteca para crear pruebas de unidades locales, que normalmente verifican el comportamiento de las clases de tu app que interactúan con Health Connect cliente.

Para comenzar a usar la biblioteca, agrégala como dependencia de prueba:

 testImplementation("androidx.health.connect:connect-testing:1.0.0-alpha01")

El punto de entrada a la biblioteca es la clase FakeHealthConnectClient, que usar en las pruebas para reemplazar HealthConnectClient. El FakeHealthConnectClient tiene las siguientes funciones:

  • Es una representación de registros en la memoria para que puedas insertar, quitar, borrar y leerlos
  • Generación de tokens de cambio y seguimiento de cambios
  • Paginación de registros y cambios
  • Las respuestas de agregación son compatibles con los stubs
  • Permite que cualquier función arroje excepciones
  • Un FakePermissionController que se puede usar para emular verificaciones de permisos

Si quieres obtener más información para reemplazar dependencias en las pruebas, lee Inyección de dependencias en Android. Para obtener más información sobre las falsificaciones, lee Cómo usar pruebas dobles en Android.

Por ejemplo, si la clase que interactúa con el cliente se llama HealthConnectManager y toma un HealthConnectClient como dependencia. se vería de la siguiente manera:

class HealthConnectManager(
    private val healthConnectClient: HealthConnectClient,
    ...
) { }

Durante las pruebas, puedes pasar una fuente falsa a la clase a prueba:

import androidx.health.connect.client.testing.ExperimentalTestingApi
import androidx.health.connect.client.testing.FakeHealthConnectClient
import kotlinx.coroutines.test.runTest

@OptIn(ExperimentalTestingApi::class)
class HealthConnectManagerTest {

    @Test
    fun readRecords_filterByActivity() = runTest {
        // Create a Fake with 2 running records.
        val fake = FakeHealthConnectClient()
        fake.insertRecords(listOf(fakeRunRecord1, fakeBikeRecord1))

        // Create a manager that depends on the fake.
        val manager = HealthConnectManager(fake)

        // Read running records only.
        val runningRecords = manager.fetchReport(activity = Running)

        // Verify that the records were filtered correctly.
        assertTrue(runningRecords.size == 1)
    }
}

Esta prueba verifica que la función fetchReport ficticia en HealthConnectManager filtra correctamente los registros por actividad.

Verifica excepciones

Casi todas las llamadas a HealthConnectClient pueden generar excepciones. Por ejemplo: la documentación de insertRecords menciona estas excepciones:

  • @throws android.os.RemoteException para cualquier falla de transporte de IPC.
  • @throws SecurityException para solicitudes con acceso no permitido.
  • @throws java.io.IOException para cualquier problema de E/S del disco.

Estas excepciones abarcan casos como mala conexión o no queda espacio en la dispositivo. Tu app debe reaccionar correctamente ante estos problemas de tiempo de ejecución, ya que pueden suceder en cualquier momento.

import androidx.health.connect.client.testing.stubs.stub

@Test
fun addRecords_throwsRemoteException_errorIsExposed() {
    // Create Fake that throws a RemoteException
    // when insertRecords is called.
    val fake = FakeHealthConnectClient()
    fake.overrides.insertRecords = stub { throw RemoteException() }

    // Create a manager that depends on the fake.
    val manager = HealthConnectManager(fake)

    // Insert a record.
    manager.addRecords(fakeRunRecord1)

    // Verify that the manager is exposing an error.
    assertTrue(manager.errors.size == 1)
}

Agregación

Las llamadas de agregación no tienen implementaciones falsas. En cambio, las llamadas de agregación usar stubs que puedas programar para que se comporten de cierta manera. Puedes acceder a la a través de la propiedad overrides de FakeHealthConnectClient.

Por ejemplo, puedes programar la función de agregación para que devuelva un resultado específico:

import androidx.health.connect.client.testing.AggregationResult
import androidx.health.connect.client.records.HeartRateRecord
import androidx.health.connect.client.records.ExerciseSessionRecord
import java.time.Duration

@Test
fun aggregate() {
    // Create a fake result.
    val result =
        AggregationResult(metrics =
            buildMap {
                put(HeartRateRecord.BPM_AVG, 74.0)
                put(
                    ExerciseSessionRecord.EXERCISE_DURATION_TOTAL,
                    Duration.ofMinutes(30)
                )
            }
        )

    // Create a fake that always returns the fake
    // result when aggregate() is called.
    val fake = FakeHealthConnectClient()
    fake.overrides.aggregate = stub(result)

Luego, puedes verificar que la clase que estás probando, HealthConnectManager en esta , procesó el resultado correctamente:

// Create a manager that depends on the fake.
val manager = HealthConnectManager(fake)
// Call the function that in turn calls aggregate on the client.
val report = manager.getHeartRateReport()

// Verify that the manager is exposing an error.
assertThat(report.bpmAverage).isEqualTo(74.0)

Permisos

La biblioteca de pruebas incluye un FakePermissionController, que se puede pasar como una dependencia de FakeHealthConnectClient.

El sujeto de prueba puede usar la PermissionController—through Propiedad permissionController de la interfaz HealthConnectClient para verificar de permisos. Esto suele hacerse antes de cada llamada al cliente.

Para probar esta funcionalidad, puedes configurar los permisos disponibles con FakePermissionController:

import androidx.health.connect.client.testing.FakePermissionController

@Test
fun newRecords_noPermissions_errorIsExposed() {
    // Create a permission controller with no permissions.
    val permissionController = FakePermissionController(grantAll = false)

    // Create a fake client with the permission controller.
    val fake = FakeHealthConnectClient(permissionController = permissionController)

    // Create a manager that depends on the fake.
    val manager = HealthConnectManager(fake)

    // Call addRecords so that the permission check is made.
    manager.addRecords(fakeRunRecord1)

    // Verify that the manager is exposing an error.
    assertThat(manager.errors).hasSize(1)
}

Paginación

La paginación es una fuente muy común de errores, por lo que FakeHealthConnectClient proporciona mecanismos que te ayudarán a verificar que tu implementación de paginación para y cambios se comporten de forma correcta.

El sujeto de prueba, HealthConnectManager en nuestro ejemplo, puede especificar la tamaño de la página en ReadRecordsRequest:

fun fetchRecordsReport(pageSize: Int = 1000) }
    val pagedRequest =
        ReadRecordsRequest(
            timeRangeFilter = ...,
            recordType = ...,
            pageToken = page1.pageToken,
            pageSize = pageSize,
        )
    val page = client.readRecords(pagedRequest)
    ...

Configurar el tamaño de la página en un valor pequeño, como 2, le permite probar fácilmente la paginación. Por ejemplo, puedes insertar 5 registros para que readRecords muestre 3. páginas diferentes:

@Test
fun readRecords_multiplePages() = runTest {

    // Create a Fake with 2 running records.
    val fake = FakeHealthConnectClient()
    fake.insertRecords(generateRunningRecords(5))

    // Create a manager that depends on the fake.
    val manager = HealthConnectManager(fake)

    // Read records with a page size of 2.
    val report = manager.generateReport(pageSize = 2)

    // Verify that all the pages were processed correctly.
    assertTrue(report.records.size == 5)
}

Datos de pruebas

La biblioteca aún no incluye APIs para generar datos falsos, pero puedes usar la los datos y los generadores que usa la biblioteca en la Búsqueda de código fuente público de Android.

Talones

La propiedad overrides de FakeHealthConnectClient te permite programar (o stub) de cualquiera de sus funciones para que arrojen excepciones cuando se las llame. Las llamadas de agregación también pueden devolver datos arbitrarios y admiten la puesta en cola múltiples respuestas. Consulta Stub y MutableStub para obtener más información.

Resumen de casos extremos

  • Verifica que tu app se comporte como se espera cuando el cliente lance excepciones. Revisa la documentación de cada función para identificar las excepciones que debe verificar.
  • Verifica que cada llamada que le hagas al cliente esté precedida por el la verificación de permisos.
  • Verifica tu implementación de paginación.
  • Verifica lo que sucede cuando recuperas varias páginas, pero una venció token.