إنشاء اختبارات للوحدات باستخدام مكتبة Health Connect Testing

مكتبة "اختبارات Health Connect" (androidx.health.connect:connect-testing) يبسط عملية إنشاء الاختبارات التلقائية. يمكنك استخدام هذه المكتبة لإثبات الملكية. سلوك تطبيقك والتحقق من استجابته بشكل صحيح والحالات غير الشائعة، والتي يصعب اختبارها يدويًا.

يمكنك استخدام المكتبة لإنشاء اختبارات الوحدات المحلية، التي يتم عادةً إثبات ملكيتها سلوك الصفوف في تطبيقك التي تتفاعل مع Health Connect العميل.

لبدء استخدام المكتبة، أضفها كتبعية اختبارية:

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

نقطة الدخول إلى المكتبة هي الصف FakeHealthConnectClient، الذي استخدامها في الاختبارات لاستبدال HealthConnectClient. FakeHealthConnectClient على الميزات التالية:

  • تمثيل في الذاكرة للسجلات، حتى تتمكن من إدراج وإزالة وحذف قراءتها
  • إنشاء رموز التغيير وتتبُّع التغييرات
  • التقسيم على صفحات للسجلّات والتغييرات
  • تتوافق استجابات التجميع مع الرموز البديلة.
  • السماح لأي دالة بطرح الاستثناءات
  • عبارة عن FakePermissionController يمكن استخدامه لمحاكاة عمليات التحقّق من الأذونات

لمعرفة المزيد حول استبدال التبعيات في الاختبارات، اقرأ تطبيق Dependency Injection في 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)
}

التجميع

ليس لطلبات التجميع عمليات تنفيذ وهمية. بدلاً من ذلك، يستدعي التجميع استخدام البذور التي يمكنك برمجتها للتصرف بطريقة معينة. يمكنك الوصول إلى صفحة التلميحات في السمة 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)
}

بيانات الاختبار

لا تتضمّن المكتبة واجهات برمجة تطبيقات لإنشاء بيانات مزيفة حتى الآن، ولكن يمكنك استخدام البيانات والأدوات التي تستخدمها المكتبة في Android Code Search.

البذور

تسمح لك السمة overrides في FakeHealthConnectClient ببرمجة (أو إيقاف) أي من دوالها بحيث تكون لها استثناءات عند استدعائها. يمكن أن تؤدي استدعاءات التجميع أيضًا إلى عرض بيانات عشوائية، كما أنها تتيح الإضافة إلى قائمة الانتظار. إجابات متعددة. يمكنك الانتقال إلى Stub وMutableStub للاطّلاع على مزيد من المعلومات.

ملخص الحالات الهامشية

  • التأكّد من أنّ تطبيقك يعمل على النحو المتوقّع عندما يطرح العميل استثناءات تحقق من وثائق كل دالة لمعرفة الاستثناءات التي التحقق من ذلك.
  • تحقق من أن كل استدعاء تقوم به للعميل يسبقه التحقق من الأذونات.
  • تأكّد من صحة تنفيذ التقسيم على صفحات.
  • تحقَّق مما يحدث عند جلب صفحات متعددة ولكن انتهت صلاحية صفحة واحدة. الرمز المميز.