יצירת בדיקות יחידה באמצעות ספריית הבדיקות של Health Connect

ספריית הבדיקות של Health Connect (androidx.health.connect:connect-testing) מפשט את היצירה של בדיקות אוטומטיות. אפשר להשתמש בספרייה הזו כדי לאמת התנהגות האפליקציה ולוודא שהיא מגיבה כראוי במקרים לא נפוצים, שקשה לבדוק אותם באופן ידני.

אפשר להשתמש בספרייה כדי ליצור בדיקות יחידות מקומיות, שבדרך כלל מאמתות התנהגות הכיתות באפליקציה שלך שמקיימים אינטראקציה עם Health Connect לקוח.

כדי להתחיל להשתמש בספרייה, צריך להוסיף אותה כתלות לבדיקה:

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

נקודת הכניסה לספרייה היא הכיתה FakeHealthConnectClient, להשתמש בו בבדיקות שיחליפו את HealthConnectClient. FakeHealthConnectClient כולל את התכונות הבאות:

  • ייצוג בזיכרון של רשומות, כך שניתן להוסיף, להסיר, למחוק לקרוא אותן
  • יצירת אסימוני שינוי ומעקב אחר שינויים
  • החלוקה לדפים של רשומות ושינויים
  • יש תמיכה בתשובות מצטברות עם stubs
  • מאפשרת לכל פונקציה לזרוק חריגים
  • FakePermissionController שיכול לשמש לאמולציה של בדיקות הרשאות

למידע נוסף על החלפת יחסי תלות בבדיקות, אפשר לקרוא החדרת תלות ב-Android. כדי לקבל מידע נוסף על זיופים, אפשר לקרוא את המאמר שימוש במכפילים לבדיקה ב-Android.

לדוגמה, אם המחלקה שמקיימת אינטראקציה עם הלקוח נקראת HealthConnectManager והוא לוקח HealthConnectClient כתלות, ייראה כך:

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

בבדיקות, אתם יכולים במקום זאת להעביר תוכן מזויף לכיתה שלכם:

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

הבדיקה הזו מאמתת שהפונקציה fetchReport הפיקטיבית HealthConnectManager מסננת כראוי רשומות לפי פעילות.

אימות חריגים

כמעט כל שיחה אל HealthConnectClient יכולה לגרום חריגות. לדוגמה, התיעוד של insertRecords כולל את החריגים הבאים:

  • @throws android.os.RemoteException במקרה של כשלים בהובלה של IPC.
  • @throws SecurityException לבקשות עם גישה ללא הרשאה.
  • @throws java.io.IOException לכל בעיה של קלט/פלט בדיסק.

החריגים האלה מתייחסים למקרים כמו חיבור לא תקין או שלא נשאר מקום במכשיר. האפליקציה שלך צריכה להגיב נכון לבעיות האלה בסביבת זמן הריצה, כי הן יכולות בכל רגע נתון.

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

צבירה

לקריאות לצבירת נתונים אין הטמעות מזויפות. במקום זאת, קריאות לצבירת נתונים להשתמש ב-stubs שניתן לתכנת כדי להתנהג באופן מסוים. אפשר לגשת stubs דרך המאפיין overrides של FakeHealthConnectClient.

לדוגמה, תוכלו לתכנת את פונקציית הצבירה כדי להחזיר תוצאה ספציפית:

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)

לאחר מכן אפשר יהיה לאמת שהכיתה בבדיקה, HealthConnectManager עיבדנו את התוצאה כמו שצריך:

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

הרשאות

ספריית הבדיקות כוללת FakePermissionController, שניתן להעביר כתלות ב-FakeHealthConnectClient.

הנושא שנבדק יכול להשתמש בPermissionController—through המאפיין permissionController של הממשק HealthConnectClient – כדי לבדוק להרשאות. הפעולה הזו מתבצעת בדרך כלל לפני כל שיחה ללקוח.

כדי לבדוק את הפונקציונליות הזו, אפשר להגדיר אילו הרשאות יהיו זמינות באמצעות 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)
}

עימוד

העימוד הוא מקור נפוץ מאוד לבאגים, לכן FakeHealthConnectClient מספקת מנגנונים שיעזרו לכם לוודא שהטמעת החלוקה לדפים רשומות ושינויים פועלים כראוי.

הנושא שנבדק, HealthConnectManager בדוגמה שלנו, יכול לציין את גודל הדף בReadRecordsRequest:

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

כשמגדירים את גודל הדף לערך קטן, כמו 2, אפשר לבדוק בקלות החלוקה לדפים. לדוגמה, אפשר להוסיף 5 רשומות כדי ש-readRecords יחזיר 3 דפים שונים:

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

נתוני הבדיקה

הספרייה עדיין לא כוללת ממשקי API ליצירת נתונים מזויפים, אבל אפשר להשתמש נתונים וגנרטורים שבהם הספרייה משתמשת בחיפוש קוד של Android.

לשוניות

המאפיין overrides של FakeHealthConnectClient מאפשר לך לתכנת (או stub out) של הפונקציה, כך שהן יקפיצו חריגות כשהן נקראות. קריאות לצבירת נתונים יכולות גם להחזיר נתונים שרירותיים, והיא תומכת ביצירת תורים תשובות מרובות. למידע נוסף, ראו Stub וMutableStub.

סיכום של מקרי קצה

  • לוודא שהאפליקציה פועלת כצפוי כשהלקוח מפעיל חריגים. מומלץ לעיין במסמכי התיעוד של כל פונקציה כדי להבין אילו חריגים צריך לבדוק.
  • לוודא שכל שיחה שאתם מבצעים ללקוח מופיעה לפני בדיקת הרשאות.
  • יש לאמת את הטמעת העימוד.
  • צריך לבדוק מה קורה כשמאחזרים כמה דפים שהתוקף שלהם פג ב-Assistant.