Twórz testy jednostkowe za pomocą biblioteki Testów Health Connect

Biblioteka testowa Health Connect (androidx.health.connect:connect-testing) ułatwia tworzenie testów automatycznych. Możesz użyć tej biblioteki, aby sprawdzić działanie aplikacji i potwierdzić, że prawidłowo reaguje ona na nietypowe przypadki, które trudno jest przetestować ręcznie.

Za pomocą tej biblioteki możesz tworzyć lokalne testy jednostkowe, które zwykle weryfikują działanie klas w aplikacji, które wchodzą w interakcję z klientem Health Connect.

Aby zacząć korzystać z biblioteki, dodaj ją jako zależność testową:

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

Punktem wejścia do biblioteki jest klasa FakeHealthConnectClient, której używasz w testach do zastąpienia klasy HealthConnectClient. FakeHealthConnectClient ma te funkcje:

  • Reprezentacja rekordów w pamięci, dzięki czemu możesz je wstawiać, usuwać i odczytywać.
  • Generowanie tokenów zmian i śledzenie zmian
  • Podział na strony dotyczący rekordów i zmian
  • Odpowiedzi agregacji są obsługiwane w przypadku elementów zastępczych
  • Zezwala na zgłaszanie wyjątków przez dowolną funkcję
  • FakePermissionController, którego można użyć do emulowania sprawdzania uprawnień.

Więcej informacji o zastępowaniu zależności w testach znajdziesz w artykule Wstrzykiwanie zależności w Androidzie. Więcej informacji o obiektach zastępczych znajdziesz w artykule Używanie obiektów zastępczych w Androidzie.

Jeśli na przykład klasa, która wchodzi w interakcję z klientem, ma nazwę HealthConnectManager i jako zależność przyjmuje HealthConnectClient, będzie wyglądać tak:

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

W testach możesz zamiast tego przekazać do testowanej klasy obiekt zastępczy:

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

Ten test sprawdza, czy fikcyjna funkcja fetchReportHealthConnectManager prawidłowo filtruje rekordy według aktywności.

Weryfikowanie wyjątków

Prawie każde wywołanie funkcji HealthConnectClient może zgłosić wyjątek. Na przykład dokumentacja insertRecords zawiera te wyjątki:

  • @throws android.os.RemoteException w przypadku niepowodzeń związanych z transportem IPC.
  • @throws SecurityException w przypadku próśb z nieautoryzowanym dostępem.
  • @throws java.io.IOException w przypadku problemów z operacjami wejścia/wyjścia dysku.

Wyjątki te obejmują przypadki takie jak słabe połączenie lub brak miejsca na urządzeniu. Aplikacja musi prawidłowo reagować na te problemy w czasie działania, ponieważ mogą one wystąpić w dowolnym momencie.

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

Agregacja

Wywołania agregacji nie mają fałszywych implementacji. Zamiast tego wywołania agregacji używają stubów, które możesz zaprogramować tak, aby zachowywały się w określony sposób. Dostęp do elementów zastępczych możesz uzyskać za pomocą właściwości overrides obiektu FakeHealthConnectClient.

Możesz na przykład zaprogramować funkcję agregującą tak, aby zwracała określony wynik:

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)

Następnie możesz sprawdzić, czy testowana klasa, w tym przypadku HealthConnectManager, prawidłowo przetworzyła wynik:

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

Uprawnienia

Biblioteka testowa zawiera element FakePermissionController, który można przekazać jako zależność do elementu FakeHealthConnectClient.

Osoba testująca może użyć właściwości PermissionController—throughpermissionController interfejsu HealthConnectClient, aby sprawdzić uprawnienia. Zwykle odbywa się to przed każdym wywołaniem klienta.

Aby przetestować tę funkcję, możesz ustawić, które uprawnienia są dostępne, za pomocą 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)
}

Podział na strony

Stronicowanie jest bardzo częstym źródłem błędów, dlatego FakeHealthConnectClientudostępnia mechanizmy, które pomagają sprawdzić, czy implementacja stronicowania rekordów i zmian działa prawidłowo.

Testowany obiekt, w naszym przykładzie HealthConnectManager, może określić rozmiar strony w ReadRecordsRequest:

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

Ustawienie małej wartości rozmiaru strony, np. 2, umożliwia testowanie paginacji. Możesz na przykład wstawić 5 rekordów, aby funkcja readRecords zwracała 3 różne strony:

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

Dane testowe

Biblioteka nie zawiera jeszcze interfejsów API do generowania fałszywych danych, ale możesz używać danych i generatorów używanych przez bibliotekę w Android Code Search.

Aby w testach symulować wartości metadanych, możesz użyć MetadataTestHelper. Zapewnia to funkcję rozszerzenia populatedWithTestValues(), która symuluje wypełnianie wartości metadanych przez Health Connect podczas wstawiania rekordu.

Stubs

Właściwość overrides obiektu FakeHealthConnectClient umożliwia zaprogramowanie (lub zaimplementowanie) dowolnej z jego funkcji tak, aby w momencie wywołania zgłaszały wyjątki. Wywołania agregacji mogą też zwracać dowolne dane i obsługują kolejkowanie wielu odpowiedzi. Więcej informacji znajdziesz w sekcjach StubMutableStub.

Podsumowanie przypadków brzegowych

  • Sprawdź, czy aplikacja działa zgodnie z oczekiwaniami, gdy klient zgłasza wyjątki. Zapoznaj się z dokumentacją każdej funkcji, aby dowiedzieć się, jakie wyjątki należy sprawdzić.
  • Sprawdź, czy każde wywołanie klienta jest poprzedzone odpowiednim sprawdzeniem uprawnień.
  • Sprawdź implementację paginacji.
  • Sprawdź, co się stanie, gdy pobierzesz wiele stron, ale jedna z nich ma wygasły token.