יצירת בדיקות יחידה באמצעות ספריית הבדיקות של 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 Code Search.

stubs

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

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

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