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. Za pomocą tej biblioteki możesz zweryfikować działania aplikacji i sprawdzanie, czy odpowiada ona prawidłowo nietypowe przypadki, 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żyć w testach, aby zastąpić 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 ich śledzenie
  • Podział rekordów i zmian na strony
  • Odpowiedzi agregacji są obsługiwane z krotkami
  • 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 Aby dowiedzieć się więcej o podróbkach, przeczytaj artykuł Korzystanie z podwójnych testów na Androidzie

Jeśli na przykład klasa wchodząca w interakcję z klientem jest wywoływana HealthConnectManager i wymaga HealthConnectClient jako zależność, będzie wyglądać tak:

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

W ramach testów możesz zamiast tego przekazać fałszywe treści klasie, które są testowane:

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

W ramach tego testu sprawdzamy, czy fikcyjna funkcja fetchReport w 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ą np. słabego połączenia lub braku miejsca na stronie 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 z wycinkami, które możesz zaprogramować tak, aby działały w określony sposób. Aby uzyskać dostęp do za pomocą właściwości overrides elementu 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.

Testowany obiekt może korzystać z: PermissionController—through właściwość permissionController interfejsu HealthConnectClient – do sprawdzenia uprawnień. Zwykle robi się to przed każdym połączeniem z klientem.

Aby przetestować tę funkcję, możesz określić, jakie uprawnienia mają być dostępne w 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

Podział na strony jest bardzo częstym źródłem błędów, więc FakeHealthConnectClient udostępnia mechanizmy, które pomagają sprawdzić, czy implementacja stronicowania a rekordy i zmiany działają prawidłowo.

Sprawdzany obiekt (HealthConnectManager w naszym przykładzie) 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 (np. 2) na stronie umożliwia łatwe testowanie podział na strony. Możesz na przykład wstawić 5 rekordów, tak aby funkcja readRecords zwracała 3 rekordy 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żyć dane i generatory używane przez bibliotekę w wyszukiwarce kodu na Androidzie.

Fragmenty

Właściwość overrides elementu FakeHealthConnectClient umożliwia programowanie (lub wytnij) dowolną z jej funkcji, tak aby po wywołaniu były zgłaszane wyjątki. 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. Zapoznaj się z dokumentacją poszczególnych funkcji, aby dowiedzieć się, jakich wyjątków powinien sprawdzić.
  • Sprawdź, czy każde połączenie z klientem jest poprzedzone odpowiednim sprawdzić uprawnienia.
  • Sprawdź implementację podziału na strony.
  • Sprawdź, co się dzieje w przypadku pobrania wielu stron, ale jedna z nich straciła ważność token.