تسهّل مكتبة Health Connect Testing (androidx.health.connect:connect-testing) عملية إنشاء اختبارات مبرمَجة. يمكنك استخدام هذه المكتبة للتحقّق من سلوك تطبيقك والتأكّد من أنّه يستجيب بشكل صحيح للحالات غير الشائعة التي يصعب اختبارها يدويًا.
يمكنك استخدام المكتبة لإنشاء اختبارات وحدة محلية تتحقّق عادةً من سلوك الفئات في تطبيقك التي تتفاعل مع برنامج Health Connect.
لبدء استخدام المكتبة، أضِفها كعنصر تابع للاختبار:
testImplementation("androidx.health.connect:connect-testing:1.0.0-alpha03")
نقطة الدخول إلى المكتبة هي الفئة FakeHealthConnectClient، والتي تستخدمها في الاختبارات لاستبدال HealthConnectClient. تتضمّن FakeHealthConnectClient الميزات التالية:
- تمثيل السجلّات في الذاكرة، ما يتيح لك إدراجها وإزالتها وحذفها وقراءتها
- إنشاء رموز مميّزة للتغيير وتتبُّع التغييرات
- تقسيم السجلّات والتغييرات على صفحات
- تتوفّر ردود التجميع مع عناصر نائبة
- يسمح لأي دالة بعرض استثناءات
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في حال حدوث أي مشاكل في وسائل النقل التي توفّرها "هيئة النقل العام"-
@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 التي يمكن الوصول إليها من خلال السمة 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.
لمحاكاة قيم بيانات التعريف في الاختبارات، يمكنك استخدام
MetadataTestHelper. يوفّر ذلك دالة الإضافة
populatedWithTestValues() التي تحاكي عملية ملء Health Connect لقيم البيانات الوصفية أثناء إدراج السجلّ.
Stubs
تتيح لك السمة overrides الخاصة بالكائن FakeHealthConnectClient برمجة (أو
إنشاء نسخة تجريبية) أي من وظائفه بحيث تعرض استثناءات عند استدعائها.
يمكن أن تعرض طلبات التجميع أيضًا بيانات عشوائية، كما تتيح وضع ردود متعددة في قائمة الانتظار. يمكنك الاطّلاع على Stub وMutableStub لمزيد من المعلومات.
ملخّص الحالات الهامشية
- تأكَّد من أنّ تطبيقك يتصرّف على النحو المتوقّع عندما يطرح العميل استثناءات. راجِع مستندات كل دالة لمعرفة الاستثناءات التي يجب التحقّق منها.
- تأكَّد من أنّ كل طلب ترسله إلى العميل يسبقه التحقّق من الأذونات المناسبة.
- تأكَّد من صحة عملية تنفيذ الترقيم.
- تحقَّق مما يحدث عند جلب صفحات متعددة ولكن إحداها تتضمّن رمزًا مميزًا منتهي الصلاحية.