Créer des tests unitaires à l'aide de la bibliothèque de tests Santé Connect

La bibliothèque de test Santé Connect (androidx.health.connect:connect-testing) simplifie la création de tests automatisés. Vous pouvez utiliser cette bibliothèque pour vérifier le comportement de votre application et valider qu'elle répond correctement aux cas inhabituels, qui sont difficiles à tester manuellement.

Vous pouvez utiliser la bibliothèque pour créer des tests unitaires locaux, qui vérifient généralement le comportement des classes de votre application qui interagissent avec le client Health Connect.

Pour commencer à utiliser la bibliothèque, ajoutez-la en tant que dépendance de test :

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

Le point d'entrée de la bibliothèque est la classe FakeHealthConnectClient, que vous utilisez dans les tests pour remplacer HealthConnectClient. FakeHealthConnectClient présente les caractéristiques suivantes :

  • Représentation en mémoire des enregistrements, qui vous permet de les insérer, de les supprimer et de les lire
  • Génération de jetons de modification et suivi des modifications
  • Pagination pour les enregistrements et les modifications
  • Les réponses d'agrégation sont acceptées avec les stubs.
  • Autorise toute fonction à générer des exceptions
  • Un FakePermissionController pouvant être utilisé pour émuler les vérifications des autorisations

Pour en savoir plus sur le remplacement des dépendances dans les tests, consultez Injection de dépendances dans Android. Pour en savoir plus sur les faux, consultez Utiliser des doubles de test sur Android.

Par exemple, si la classe qui interagit avec le client est appelée HealthConnectManager et qu'elle prend un HealthConnectClient comme dépendance, elle ressemblerait à ceci :

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

Dans les tests, vous pouvez transmettre un faux à votre classe en cours de test :

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)
    }
}

Ce test vérifie que la fonction fictive fetchReport dans HealthConnectManager filtre correctement les enregistrements par activité.

Vérifier les exceptions

Presque tous les appels à HealthConnectClient peuvent générer des exceptions. Par exemple, la documentation de insertRecords mentionne les exceptions suivantes :

  • @throws android.os.RemoteException pour tout échec de transport IPC.
  • @throws SecurityException pour les demandes avec accès non autorisé.
  • @throws java.io.IOException pour tout problème d'E/S de disque.

Ces exceptions couvrent des cas tels qu'une mauvaise connexion ou un espace de stockage insuffisant sur l'appareil. Votre application doit réagir correctement à ces problèmes d'exécution, car ils peuvent survenir à tout moment.

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)
}

Agrégation

Les appels d'agrégation n'ont pas d'implémentations fictives. Au lieu de cela, les appels d'agrégation utilisent des stubs que vous pouvez programmer pour qu'ils se comportent d'une certaine manière. Vous pouvez accéder aux stubs via la propriété overrides de FakeHealthConnectClient.

Par exemple, vous pouvez programmer la fonction d'agrégation pour qu'elle renvoie un résultat spécifique :

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)

Vous pouvez ensuite vérifier que votre classe testée, HealthConnectManager dans ce cas, a correctement traité le résultat :

// 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)

Autorisations

La bibliothèque de test inclut un FakePermissionController, qui peut être transmis en tant que dépendance à FakeHealthConnectClient.

Votre sujet de test peut utiliser la propriété permissionController de l'interface HealthConnectClient pour vérifier les autorisations.PermissionController—through Cette opération est généralement effectuée avant chaque appel au client.

Pour tester cette fonctionnalité, vous pouvez définir les autorisations disponibles à l'aide de 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)
}

Pagination

La pagination est une source très courante de bugs. FakeHealthConnectClient fournit donc des mécanismes pour vous aider à vérifier que votre implémentation de la pagination pour les enregistrements et les modifications se comporte correctement.

Votre sujet de test, HealthConnectManager dans notre exemple, peut spécifier la taille de la page dans ReadRecordsRequest :

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

Définir une petite taille de page, par exemple 2, vous permet de tester la pagination. Par exemple, vous pouvez insérer cinq enregistrements pour que readRecords renvoie trois pages différentes :

@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)
}

Données de test

La bibliothèque n'inclut pas encore d'API permettant de générer de fausses données, mais vous pouvez utiliser les données et les générateurs utilisés par la bibliothèque dans Android Code Search.

Pour simuler des valeurs de métadonnées dans les tests, vous pouvez utiliser MetadataTestHelper. Cela fournit la fonction d'extension populatedWithTestValues(), qui simule Health Connect en remplissant les valeurs de métadonnées lors de l'insertion d'enregistrements.

Stubs

La propriété overrides de FakeHealthConnectClient vous permet de programmer (ou de créer des stubs) n'importe laquelle de ses fonctions afin qu'elles génèrent des exceptions lorsqu'elles sont appelées. Les appels d'agrégation peuvent également renvoyer des données arbitraires et permettent de mettre en file d'attente plusieurs réponses. Pour en savoir plus, consultez Stub et MutableStub.

Résumé des cas limites

  • Vérifiez que votre application se comporte comme prévu lorsque le client génère des exceptions. Consultez la documentation de chaque fonction pour déterminer les exceptions à vérifier.
  • Vérifiez que chaque appel que vous effectuez au client est précédé de la vérification des autorisations appropriée.
  • Vérifiez l'implémentation de la pagination.
  • Vérifiez ce qui se passe lorsque vous récupérez plusieurs pages, mais que l'une d'elles possède un jeton expiré.