Membuat pengujian unit menggunakan library Pengujian Health Connect

Library Pengujian Health Connect (androidx.health.connect:connect-testing) menyederhanakan pembuatan pengujian otomatis. Anda dapat menggunakan library ini untuk memverifikasi perilaku aplikasi dan memvalidasi bahwa aplikasi merespons dengan benar kasus yang tidak umum, yang sulit diuji secara manual.

Anda dapat menggunakan library untuk membuat pengujian unit lokal, yang biasanya memverifikasi perilaku class di aplikasi Anda yang berinteraksi dengan klien Health Connect.

Untuk mulai menggunakan library, tambahkan sebagai dependensi pengujian:

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

Titik entri ke library adalah class FakeHealthConnectClient, yang Anda gunakan dalam pengujian untuk menggantikan HealthConnectClient. FakeHealthConnectClient memiliki fitur berikut:

  • Representasi data dalam memori, sehingga Anda dapat menyisipkan, menghapus, dan membaca data
  • Pembuatan token perubahan dan pelacakan perubahan
  • Penomoran halaman untuk catatan dan perubahan
  • Respons agregasi didukung dengan stub
  • Mengizinkan fungsi apa pun untuk memunculkan pengecualian
  • FakePermissionController yang dapat digunakan untuk meniru pemeriksaan izin

Untuk mempelajari lebih lanjut cara mengganti dependensi dalam pengujian, baca Injeksi Dependensi di Android. Untuk mengetahui lebih lanjut tentang tiruan, baca Menggunakan duplikat pengujian di Android.

Misalnya, jika class yang berinteraksi dengan klien disebut HealthConnectManager dan mengambil HealthConnectClient sebagai dependensi, maka akan terlihat seperti:

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

Dalam pengujian, Anda dapat meneruskan tiruan ke class yang sedang diuji:

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

Pengujian ini memverifikasi bahwa fungsi fetchReport fiktif di HealthConnectManager memfilter data dengan benar menurut aktivitas.

Memverifikasi pengecualian

Hampir setiap panggilan ke HealthConnectClient dapat memunculkan pengecualian. Misalnya, dokumentasi untuk insertRecords menyebutkan pengecualian berikut:

  • @throws android.os.RemoteException untuk setiap kegagalan pengiriman IPC.
  • @throws SecurityException untuk permintaan dengan akses yang tidak diizinkan.
  • @throws java.io.IOException untuk masalah I/O disk.

Pengecualian ini mencakup kasus seperti koneksi yang buruk atau tidak ada ruang yang tersisa di perangkat. Aplikasi Anda harus bereaksi dengan benar terhadap masalah runtime ini, karena dapat terjadi kapan saja.

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

Agregasi

Panggilan agregasi tidak memiliki implementasi palsu. Sebagai gantinya, panggilan agregasi menggunakan stub yang dapat Anda program untuk berperilaku dengan cara tertentu. Anda dapat mengakses stub melalui properti overrides dari FakeHealthConnectClient.

Misalnya, Anda dapat memprogram fungsi gabungan untuk menampilkan hasil tertentu:

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)

Kemudian, Anda dapat memverifikasi bahwa class yang sedang diuji, HealthConnectManager dalam kasus ini, memproses hasil dengan benar:

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

Izin

Library pengujian menyertakan FakePermissionController, yang dapat diteruskan sebagai dependensi ke FakeHealthConnectClient.

Subjek yang sedang diuji dapat menggunakan PermissionController—through properti permissionController dari antarmuka HealthConnectClient—untuk memeriksa izin. Hal ini biasanya dilakukan sebelum setiap panggilan ke klien.

Untuk menguji fungsi ini, Anda dapat menetapkan izin yang tersedia menggunakan 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)
}

Penomoran halaman

Penomoran halaman adalah sumber bug yang sangat umum, jadi FakeHealthConnectClient menyediakan mekanisme untuk membantu Anda memverifikasi bahwa penerapan penomoran halaman untuk rekaman dan perubahan berperilaku dengan benar.

Subjek yang sedang diuji, HealthConnectManager dalam contoh ini, dapat menentukan ukuran halaman dalam ReadRecordsRequest:

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

Menetapkan ukuran halaman ke nilai kecil, seperti 2, memungkinkan Anda menguji penomoran halaman. Misalnya, Anda dapat menyisipkan 5 data sehingga readRecords menampilkan 3 halaman yang berbeda:

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

Data pengujian

Library ini belum menyertakan API untuk membuat data palsu, tetapi Anda dapat menggunakan data dan generator yang digunakan oleh library di Penelusuran Kode Android.

Untuk meniru nilai metadata dalam pengujian, Anda dapat menggunakan MetadataTestHelper. Hal ini menyediakan fungsi ekstensi populatedWithTestValues(), yang menyimulasikan Health Connect mengisi nilai metadata selama penyisipan data.

Stub

Properti overrides dari FakeHealthConnectClient memungkinkan Anda memprogram (atau mengganti) fungsi apa pun sehingga fungsi tersebut akan menampilkan pengecualian saat dipanggil. Panggilan agregasi juga dapat menampilkan data arbitrer, dan mendukung antrean beberapa respons. Lihat Stub dan MutableStub untuk mengetahui informasi selengkapnya.

Ringkasan kasus ekstrem

  • Verifikasi bahwa aplikasi Anda berperilaku seperti yang diharapkan saat klien menampilkan pengecualian. Periksa dokumentasi setiap fungsi untuk mengetahui pengecualian yang harus Anda periksa.
  • Pastikan setiap panggilan yang Anda lakukan ke klien didahului dengan pemeriksaan izin yang tepat.
  • Verifikasi penerapan penomoran halaman Anda.
  • Verifikasi apa yang terjadi saat Anda mengambil beberapa halaman, tetapi salah satunya memiliki token yang sudah tidak berlaku.