Creare test delle unità utilizzando la libreria dei test di Connessione Salute

La libreria di test di Health Connect (androidx.health.connect:connect-testing) semplifica la creazione di test automatici. Puoi utilizzare questa libreria per verificare il comportamento della tua applicazione e convalidare che risponda correttamente a casi insoliti, difficili da testare manualmente.

Puoi utilizzare la libreria per creare test delle unità locali, che in genere verificano il comportamento delle classi nella tua app che interagiscono con il client Health Connect.

Per iniziare a utilizzare la libreria, aggiungila come dipendenza di test:

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

Il punto di accesso alla libreria è la classe FakeHealthConnectClient, che utilizzi nei test per sostituire HealthConnectClient. FakeHealthConnectClient ha le seguenti funzionalità:

  • Una rappresentazione in memoria dei record, in modo da poterli inserire, rimuovere, eliminare e leggere.
  • Generazione di token di modifica e monitoraggio delle modifiche
  • Impaginazione per record e modifiche
  • Le risposte di aggregazione sono supportate con gli stub
  • Consente a qualsiasi funzione di generare eccezioni
  • Un FakePermissionController che può essere utilizzato per emulare i controlli delle autorizzazioni

Per scoprire di più sulla sostituzione delle dipendenze nei test, leggi Dependency Injection in Android. Per saperne di più sui test doppi, leggi Utilizzo di test doppi in Android.

Ad esempio, se la classe che interagisce con il client si chiama HealthConnectManager e accetta HealthConnectClient come dipendenza, il codice sarà simile a questo:

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

Nei test, puoi passare un falso alla tua classe in fase di 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)
    }
}

Questo test verifica che la funzione fittizia fetchReport in HealthConnectManager filtri correttamente i record in base all'attività.

Verifica delle eccezioni

Quasi tutte le chiamate a HealthConnectClient possono generare eccezioni. Ad esempio, la documentazione per insertRecords menziona queste eccezioni:

  • @throws android.os.RemoteException per eventuali errori di trasporto IPC.
  • @throws SecurityException per le richieste con accesso non consentito.
  • @throws java.io.IOException per eventuali problemi di I/O del disco.

Queste eccezioni riguardano casi come una connessione instabile o spazio insufficiente sul dispositivo. La tua app deve reagire correttamente a questi problemi di runtime, in quanto possono verificarsi in qualsiasi 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)
}

Aggregazione

Le chiamate di aggregazione non hanno implementazioni false. Invece, le chiamate di aggregazione utilizzano stub che puoi programmare per comportarsi in un determinato modo. Puoi accedere agli stub tramite la proprietà overrides di FakeHealthConnectClient.

Ad esempio, puoi programmare la funzione aggregata in modo che restituisca un risultato specifico:

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)

Dopodiché, puoi verificare che la classe in fase di test, HealthConnectManager in questo caso, abbia elaborato correttamente il risultato:

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

Autorizzazioni

La libreria di test include un FakePermissionController, che può essere passato come dipendenza a FakeHealthConnectClient.

Il soggetto sottoposto a test può utilizzare la proprietà PermissionController—through permissionController dell'interfaccia HealthConnectClient per verificare le autorizzazioni. In genere, questa operazione viene eseguita prima di ogni chiamata al client.

Per testare questa funzionalità, puoi impostare le autorizzazioni disponibili utilizzando 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)
}

Impaginazione

La paginazione è una fonte molto comune di bug, pertanto FakeHealthConnectClient fornisce meccanismi per aiutarti a verificare che l'implementazione della paginazione per record e modifiche si comporti correttamente.

Il soggetto in esame, HealthConnectManager nel nostro esempio, può specificare le dimensioni della pagina in ReadRecordsRequest:

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

Se imposti le dimensioni della pagina su un valore ridotto, ad esempio 2, puoi testare la paginazione. Ad esempio, puoi inserire 5 record in modo che readRecords restituisca 3 pagine diverse:

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

Dati di test

La libreria non include ancora API per generare dati falsi, ma puoi utilizzare i dati e i generatori utilizzati dalla libreria nella ricerca di codice Android.

Per simulare i valori dei metadati nei test, puoi utilizzare MetadataTestHelper. In questo modo viene fornita la funzione di estensione populatedWithTestValues(), che simula il popolamento dei valori dei metadati da parte di Connessione Salute durante l'inserimento dei record.

Stubs

La proprietà overrides di FakeHealthConnectClient consente di programmare (o stub out) una qualsiasi delle sue funzioni in modo che generino eccezioni quando vengono chiamate. Le chiamate di aggregazione possono anche restituire dati arbitrari e supportano l'accodamento di più risposte. Per saperne di più, consulta Stub e MutableStub.

Riepilogo dei casi limite

  • Verifica che la tua app si comporti come previsto quando il client genera eccezioni. Consulta la documentazione di ogni funzione per capire quali eccezioni devi controllare.
  • Verifica che ogni chiamata al client sia preceduta dal controllo delle autorizzazioni corrette.
  • Verifica l'implementazione della paginazione.
  • Verifica cosa succede quando recuperi più pagine, ma una ha un token scaduto.