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

Biblioteka testów Health Connect (androidx.health.connect:connect-testing) upraszcza tworzenie testów zautomatyzowanych. Możesz użyć tej biblioteki, aby sprawdzić zachowanie aplikacji i sprawdzić, czy reaguje ona prawidłowo w nietypowych przypadkach, które trudno jest przetestować ręcznie.

Możesz jej używać do tworzenia lokalnych testów jednostkowych, które zwykle sprawdzają zachowanie zajęć w aplikacji, które współdziałają z 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żna wstawiać, usuwać, usuwać przeczytaj je
  • Generowanie tokenów zmian i śledzenie zmian
  • Podział na strony rekordów i zmian
  • Odpowiedzi agregacji są obsługiwane za pomocą szablonów
  • Zezwala dowolnej funkcji na zgłaszanie wyjątków
  • Interfejs FakePermissionController, który może być używany do emulacji 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 podróbkach znajdziesz w artykule Korzystanie z podwójnych testów na Androidzie.

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

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

Podczas testów możesz przekazać fałszywy obiekt do testowanej klasy:

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 fetchReport w funkcji HealthConnectManager prawidłowo filtruje rekordy według aktywności.

Weryfikuję wyjątki

Prawie każde wywołanie funkcji HealthConnectClient może zgłosić wyjątki. Przykład: w dokumentacji dotyczącej domeny insertRecords wymieniono te wyjątki:

  • @throws android.os.RemoteException w przypadku awarii transportu IPC.
  • @throws SecurityException w przypadku żądań z niedozwolonym dostępem.
  • @throws java.io.IOException w przypadku problemów z I/O dysku.

Te wyjątki dotyczą takich sytuacji, jak słabe połączenie lub brak miejsca na urządzenia. Twoja aplikacja musi prawidłowo reagować na te problemy w czasie działania aplikacji, mogą wystąpić w każdej chwili.

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 agregujące nie zawierają fałszywych implementacji. Zamiast tego wywołania agregacji używać wycinków, które możesz zaprogramować, aby działały w określony sposób. Dostęp do skrótów masz w ramach właściwości overrides usługi FakeHealthConnectClient.

Na przykład funkcję agregacji można zaprogramować w taki sposób, 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 testowane zajęcia, HealthConnectManager, na udało się przetworzyć wynik prawidłowo:

// 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 zasób FakePermissionController, który można przekazać jako zależność do FakeHealthConnectClient.

Osoba badana może sprawdzić uprawnienia za pomocą właściwości PermissionController—through w usługach permissionControllerHealthConnectClient w interfejsie HealthConnectClient. Zwykle odbywa się to przed każdym wywołaniem klienta.

Aby przetestować tę funkcję, możesz ustawić dostępne uprawnienia 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 FakeHealthConnectClientzapewnia mechanizmy, które ułatwiają sprawdzanie, czy implementacja stronowania dla rekordów i zmian działa prawidłowo.

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

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

Ustawienie małego rozmiaru strony, np. 2, pozwala łatwo przetestować strony powiązane. Możesz np. 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 generowania używanych przez nią w Android Code Search.

Stubs

Właściwość overrides obiektu FakeHealthConnectClient umożliwia programowanie (lub zastępowanie) dowolnej z jego funkcji, tak aby wyrzucały wyjątki po wywołaniu. Wywołania agregacji również mogą zwracać dowolne dane i obsługują kolejkowanie wiele odpowiedzi. Więcej informacji znajdziesz w sekcjach Stub i MutableStub.

Podsumowanie przypadków skrajnych

  • Sprawdź, czy aplikacja działa zgodnie z oczekiwaniami, gdy klient zgłasza wyjątki. Aby dowiedzieć się, jakie wyjątki należy sprawdzić, zapoznaj się z dokumentacją poszczególnych funkcji.
  • Sprawdź, czy każde połączenie z klientem jest poprzedzone odpowiednim sprawdzić uprawnienia.
  • Sprawdź implementację strony z przewijaniem.
  • Sprawdź, co się dzieje w przypadku pobrania wielu stron, ale jedna z nich straciła ważność token.