تست های واحد را با استفاده از کتابخانه Health Connect Testing ایجاد کنید

کتابخانه تست Health Connect ( androidx.health.connect:connect-testing ) ایجاد تست‌های خودکار را ساده می‌کند. می‌توانید از این کتابخانه برای تأیید رفتار برنامه خود و تأیید پاسخ صحیح آن به موارد غیرمعمول که تست دستی آنها دشوار است، استفاده کنید.

شما می‌توانید از این کتابخانه برای ایجاد تست‌های واحد محلی استفاده کنید، که معمولاً رفتار کلاس‌های برنامه شما را که با کلاینت Health Connect تعامل دارند، تأیید می‌کنند.

برای شروع استفاده از کتابخانه، آن را به عنوان یک وابستگی آزمایشی اضافه کنید:

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

نقطه ورود به کتابخانه، کلاس FakeHealthConnectClient است که شما در تست‌ها برای جایگزینی HealthConnectClient از آن استفاده می‌کنید. FakeHealthConnectClient دارای ویژگی‌های زیر است:

  • نمایش درون حافظه‌ای رکوردها، بنابراین می‌توانید آنها را درج، حذف، پاک و بخوانید
  • تولید توکن‌های تغییر و ردیابی تغییرات
  • صفحه بندی برای رکوردها و تغییرات
  • پاسخ‌های تجمیعی با stubها پشتیبانی می‌شوند
  • به هر تابعی اجازه می‌دهد تا استثنائات را ایجاد کند
  • یک FakePermissionController که می‌تواند برای شبیه‌سازی بررسی مجوزها استفاده شود

برای کسب اطلاعات بیشتر در مورد جایگزینی وابستگی‌ها در تست‌ها، تزریق وابستگی در اندروید را مطالعه کنید. برای کسب اطلاعات بیشتر در مورد تست‌های جعلی، استفاده از تست‌های مضاعف در اندروید را مطالعه کنید.

برای مثال، اگر کلاسی که با کلاینت تعامل دارد HealthConnectManager نام داشته باشد و یک HealthConnectClient به عنوان وابستگی بپذیرد، به صورت زیر خواهد بود:

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

در تست‌ها، می‌توانید به جای آن، یک مقدار جعلی (fake) به کلاس تحت تست خود ارسال کنید:

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

تجمیع

فراخوانی‌های تجمیع، پیاده‌سازی‌های جعلی ندارند. در عوض، فراخوانی‌های تجمیع از stubهایی استفاده می‌کنند که می‌توانید آنها را طوری برنامه‌ریزی کنید که به شیوه‌ای خاص رفتار کنند. می‌توانید از طریق ویژگی overrides در FakeHealthConnectClient به stubها دسترسی داشته باشید.

برای مثال، می‌توانید تابع تجمیع را طوری برنامه‌ریزی کنید که یک نتیجه خاص را برگرداند:

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

تنظیم اندازه صفحه روی یک مقدار کوچک، مانند ۲، به شما امکان می‌دهد صفحه‌بندی را آزمایش کنید. برای مثال، می‌توانید ۵ رکورد وارد کنید تا readRecords سه صفحه مختلف را برگرداند:

@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 استفاده کنید.

برای شبیه‌سازی مقادیر فراداده در تست‌ها، می‌توانید از MetadataTestHelper استفاده کنید. این تابع، تابع افزونه‌ی populatedWithTestValues() را فراهم می‌کند که مقادیر فراداده‌ی Health Connect را در حین درج رکورد شبیه‌سازی می‌کند.

خردها

ویژگی overrides در FakeHealthConnectClient به شما امکان می‌دهد هر یک از توابع آن را طوری برنامه‌ریزی (یا حذف ) کنید که هنگام فراخوانی، استثناهایی را ایجاد کنند. فراخوانی‌های Aggregation همچنین می‌توانند داده‌های دلخواه را برگردانند و از قرار دادن چندین پاسخ در صف پشتیبانی می‌کنند. برای اطلاعات بیشتر به Stub و MutableStub مراجعه کنید.

خلاصه موارد حاشیه‌ای

  • تأیید کنید که برنامه شما هنگام بروز خطا توسط کلاینت، مطابق انتظار رفتار می‌کند. مستندات هر تابع را بررسی کنید تا بفهمید کدام خطاها را باید بررسی کنید.
  • مطمئن شوید که هر تماسی که با کلاینت برقرار می‌کنید، قبل از آن مجوزهای مربوطه بررسی شده باشد.
  • پیاده‌سازی صفحه‌بندی خود را تأیید کنید.
  • بررسی کنید چه اتفاقی می‌افتد وقتی چندین صفحه را دریافت می‌کنید اما یکی از آنها توکن منقضی شده دارد.