پیکربندی محیط آزمایش با v2 testing APIs

نسخه‌های v2 از «میاناهای برنامه‌سازی کاربردی» آزمایش Compose (createComposeRule، createAndroidComposeRule، runComposeUiTest، runAndroidComposeUiTest، و غیره) اکنون برای بهبود کنترل بر اجرای روتین همکار دردسترس است. این به‌روزرسانی کل سطح میانای برنامه‌سازی کاربردی را تکرار نمی‌کند؛ فقط میاناهای برنامه‌سازی کاربردی که محیط آزمایش را ایجاد می‌کنند به‌روزرسانی شده‌اند.

«میاناهای برنامه‌سازی کاربردی» نسخه ۱ منسوخ شده است و قویاً توصیه می‌شود به «میاناهای برنامه‌سازی کاربردی» جدید انتقال دهید. انتقال تأیید می‌کند که آزمون‌هایتان با عملکرد استاندارد روتین هم‌راستا است و از مشکلات سازگاری در آینده جلوگیری می‌کند. برای فهرست میاناهای برنامه‌سازی کاربردی v1 منسوخ‌شده، نگاشت‌های میانای برنامه‌سازی کاربردی را ببینید.

درحالی‌که «میاناهای برنامه‌سازی کاربردی» نسخه v1 به UnconfinedTestDispatcher متکی بودند، «میاناهای برنامه‌سازی کاربردی» نسخه v2 به‌طور پیش‌فرض از StandardTestDispatcher برای ترکیب درحال اجرا استفاده می‌کنند. این تغییر رفتار آزمایشی Compose را با میاناهای برنامه‌سازی کاربردی استاندارد runTest هماهنگ می‌کند و کنترل صریحی بر ترتیب اجرای روتین‌های همکار فراهم می‌کند.

پیکربندی محیط آزمایش

«میاناهای برنامه‌سازی کاربردی» نسخه ۲ آزمون Compose از ComposeUiTestConfig برای سفارشی‌سازی محیط آزمون استفاده می‌کنند. میاناهای برنامه‌سازی کاربردی که برای آزمایش‌ها کارکردهای راه‌اندازی ایجاد می‌کنند، مثل createComposeRule، runComposeUiTest، و دیگر میاناهای برنامه‌سازی کاربردی مرتبط، ComposeUiTestConfig را می‌پذیرند. این شیء پیکربندی میاناهای برنامه‌سازی کاربردی مرتبط با محیط را مثل effectContext، runTestContext، و testTimeout در یک شیء واحد ادغام می‌کند.

مدل پیکربندی inputMode را نیز مدیریت می‌کند. میاناهای برنامه‌سازی کاربردی Compose test v2 به‌طور پیش‌فرض در ابتدای هر آزمایش InputMode.Touch را اعمال می‌کند تا قطعیت را تضمین کند و از نشت وضعیت حالت ورودی بین آزمایش‌ها جلوگیری کند.

‫ComposeUiTestConfig بخشی از «میاناهای برنامه‌سازی کاربردی» نسخه ۲ آزمایش Compose است که به‌طور پیش‌فرض از StandardTestDispatcher استفاده می‌کند. اگر آزمایش‌هایتان از میاناهای برنامه‌سازی کاربردی v1 استفاده می‌کند، قبل‌از اتخاذ ComposeUiTestConfig، انتقال به میاناهای برنامه‌سازی کاربردی آزمایش v2 را ببینید.

انتقال به ComposeUiTestConfig

در سربارهای ایجاد توابع راه‌اندازی در آزمایش‌ها، چندین سربار که پارامترهای پیکربندی جداگانه را می‌پذیرند -- مثل effectContext، runTestContext، یا testTimeout -- منسوخ شده‌اند. آزمایش‌هایتان را به‌روز کنید تا به‌جای آن از ComposeUiTestConfig استفاده کنید، همان‌طور که در مثال زیر نشان داده شده است:

حالت ورودی پیش‌فرض

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

برای حل کردن این مشکل، حالت ورودی موردنیاز را در ComposeUiTestConfig مشخص کنید:

class FocusTest {
    @get:Rule
    val rule = createComposeRule(
        config = ComposeUiTestConfig(inputMode = InputMode.Keyboard)
    )

    @Test
    fun testFocus() {}
}

برای پیکربندی حالت ورودی برای موارد آزمون جداگانه به‌جای کل کلاس آزمون، ComposeUiTestConfig را به runComposeUiTest ارسال کنید:

class FocusTest {
    @Test
    fun testTouchMode() = runComposeUiTest {
        // Runs with the default InputMode.Touch
    }

    @Test
    fun testKeyboardMode() = runComposeUiTest(
        ComposeUiTestConfig(inputMode = InputMode.Keyboard)
    ) {
        // Runs with InputMode.Keyboard
    }
}

برای سایر مشکلات و راه‌حل‌های انتقال، به خطاهای رایج و نحوه رفع آن‌ها مراجعه کنید.

انتقال به میاناهای برنامه‌سازی کاربردی آزمایش نسخه ۲

هنگام ارتقا به v2 APIs، به‌طورکلی می‌توانید از یافتن + جایگزین کردن برای به‌روزرسانی واردات بسته و پذیرفتن تغییرات جدید توزیع‌کننده استفاده کنید.

یا اینکه از Gemini بخواهید با پیام‌واره زیر، انتقال به نسخه ۲ «میاناهای برنامه‌سازی کاربردی» Compose testing را انجام دهد:

انتقال از میاناهای برنامه‌سازی کاربردی آزمایش نسخه ۱ به میاناهای برنامه‌سازی کاربردی آزمایش نسخه ۲

این پیام‌واره از این راهنما برای انتقال به v2 testing APIs استفاده خواهد کرد.

Migrate to Compose testing v2 APIs using the official
migration guide.

استفاده از پیام‌واره‌های هوش مصنوعی

پیام‌واره‌های هوش مصنوعی برای استفاده در Gemini در «استودیو Android» درنظر گرفته شده است.

در اینجا درباره Gemini در «استودیو» بیشتر بدانید: https://developer.android.com/studio/gemini/overview

از جدول زیر برای نگاشت میاناهای برنامه‌سازی کاربردی منسوخ v1 به جایگزین‌های v2 آن‌ها استفاده کنید:

منسوخ (v1)

جایگزین (v2)

androidx.compose.ui.test.junit4.createComposeRule

androidx.compose.ui.test.junit4.v2.createComposeRule

androidx.compose.ui.test.junit4.createAndroidComposeRule

androidx.compose.ui.test.junit4.v2.createAndroidComposeRule

androidx.compose.ui.test.junit4.createEmptyComposeRule

androidx.compose.ui.test.junit4.v2.createEmptyComposeRule

androidx.compose.ui.test.junit4.AndroidComposeTestRule

androidx.compose.ui.test.junit4.v2.AndroidComposeTestRule

androidx.compose.ui.test.runComposeUiTest

androidx.compose.ui.test.v2.runComposeUiTest

androidx.compose.ui.test.runAndroidComposeUiTest

androidx.compose.ui.test.v2.runAndroidComposeUiTest

androidx.compose.ui.test.runEmptyComposeUiTest

androidx.compose.ui.test.v2.runEmptyComposeUiTest

androidx.compose.ui.test.AndroidComposeUiTestEnvironment

androidx.compose.ui.test.v2.AndroidComposeUiTestEnvironment

سازگاری با نسخه قدیمی و استثناها

«میاناهای برنامه‌سازی کاربردی» نسخه v1 کنونی اکنون منسوخ شده‌اند، اما برای حفظ عملکرد موجود و جلوگیری از تغییرات مخرب، همچنان از UnconfinedTestDispatcher استفاده کنید.

تنها استثنایی که در آن رفتار پیش‌فرض تغییر کرده است، مورد زیر است:

فرستنده آزمایشی پیش‌فرض که برای اجرای ترکیب در کلاس AndroidComposeUiTestEnvironment استفاده می‌شود از UnconfinedTestDispatcher به StandardTestDispatcher تغییر کرده است. این موضوع بر مواردی که بااستفاده از سازنده نمونه‌ای ایجاد می‌کنید یا زیرکلاسی AndroidComposeUiTestEnvironment ایجاد می‌کنید و آن سازنده را فرا می‌خوانید تأثیر می‌گذارد.

تغییر کلیدی: تأثیر بر اجرای روتین همکار

تفاوت اصلی بین نسخه ۱ و نسخه ۲ «میاناهای برنامه‌سازی کاربردی» نحوه توزیع روتین‌های همکار است:

  • میاناهای برنامه‌سازی کاربردی v1 (UnconfinedTestDispatcher): وقتی یک روال همکار راه‌اندازی می‌شد، این روال بلافاصله در رشته فعلی اجرا می‌شد و اغلب قبل‌از اجرای خط بعدی کد آزمایش تکمیل می‌شد. برخلاف رفتار تولید، این اجرای فوری می‌تواند به‌طور ناخواسته مشکلات زمان‌بندی واقعی یا شرایط مسابقه را پنهان کند که در یک برنامه زنده رخ می‌دهد.
  • میاناهای برنامه‌سازی کاربردی v2 (StandardTestDispatcher): وقتی یک روال همکار راه‌اندازی می‌شود، در صف قرار می‌گیرد و تا زمانی که آزمایش به‌طور صریح ساعت مجازی را جلو نبرد اجرا نمی‌شود. میاناهای برنامه‌سازی کاربردی استاندارد «نوشتن» (مثل waitForIdle()) ازقبل این همگام‌سازی را انجام می‌دهند، بنابراین اکثر آزمایش‌هایی که به این میاناهای برنامه‌سازی کاربردی استاندارد متکی هستند باید بدون تغییر به کار خود ادامه دهند.

خطاهای رایج و نحوه رفع آن‌ها

اگر آزمایش‌هایتان پس‌از ارتقا به نسخه ۲ ناموفق بود، احتمالاً الگوی زیر را نشان می‌دهند:

  • ناموفق: تکلیفی را راه‌اندازی می‌کنید (برای مثال، یک ViewModel داده‌ها را بار می‌کند)، اما ادعای شما بلافاصله ناموفق می‌شود زیرا داده‌ها هنوز در حالت «بار کردن» هستند.
  • علت: با «میاناهای برنامه‌سازی کاربردی» نسخه ۲، روال‌های هم‌زمان به‌جای اینکه بلافاصله اجرا شوند، در صف قرار می‌گیرند. تکلیف در صف قرار گرفت اما قبل‌از اینکه نتیجه بررسی شود، هرگز اجرا نشد.
  • رفع: زمان را به‌طور صریح جلو ببرید. باید به‌طور صریح به توزیع‌کننده v2 بگویید چه زمانی کار را اجرا کند.

رویکرد قبلی

در نسخه ۱، تکلیف بلافاصله راه‌اندازی و تمام شد. در نسخه ۲، کد زیر ناموفق است زیرا loadData() هنوز اجرا نشده است.

// In v1, this launched and finished immediately.
viewModel.loadData()

// In v2, this fails because loadData() hasn't actually run yet!
assertEquals(Success, viewModel.state.value)

از waitForIdle یا runOnIdle برای اجرای تکالیف در صف قبل‌از ادعای مالکیت استفاده کنید.

گزینه ۱: استفاده از waitForIdle ساعت را تا زمانی که واسط کاربر غیرفعال شود جلو می‌برد و اجرای روتین هم‌زمان را درستی‌سنجی می‌کند.

viewModel.loadData()

// Explicitly run all queued tasks
composeTestRule.waitForIdle()

assertEquals(Success, viewModel.state.value)

گزینه ۲: استفاده از runOnIdle بلوک کد را در رشته میانای کاربر پس‌از بیکار شدن میانای کاربر اجرا می‌کند.

viewModel.loadData()

// Run the assertion after the UI is idle
composeTestRule.runOnIdle {
    assertEquals(Success, viewModel.state.value)
}

همگام‌سازی دستی

در سناریوهایی که شامل همگام‌سازی دستی است، مثلاً زمانی که پیشروی خودکار غیرفعال است، راه‌اندازی یک روتین همکار منجر به اجرای فوری نمی‌شود زیرا ساعت آزمایش موقتاً متوقف است. برای اجرای روتین‌های همکار در صف بدون پیش بردن ساعت مجازی، از runCurrent() API استفاده کنید. این دستور تکالیف زمان مجازی کنونی را اجرا می‌کند.

composeTestRule.mainClock.scheduler.runCurrent()

برخلاف waitForIdle() که ساعت آزمایش را تا زمانی که واسط کاربر پایدار شود جلو می‌برد، runCurrent() وظایف معلقه را درحالی‌که زمان مجازی فعلی را حفظ می‌کند اجرا می‌کند. این رفتار امکان درستی‌سنجی وضعیت‌های واسطه‌ای را فراهم می‌کند که اگر ساعت به وضعیت آماده‌به‌کار پیش می‌رفت، از آن‌ها صرف‌نظر می‌شد.

زمان‌بند آزمایش زیربنایی که در محیط آزمایش استفاده می‌شود آشکار می‌شود. این زمان‌بند را می‌توان به‌همراه Kotlin runTest API برای همگام‌سازی ساعت آزمایش استفاده کرد.

انتقال به runComposeUiTest

اگر از Compose test APIs در کنار Kotlin runTest API استفاده می‌کنید، به‌شدت توصیه می‌شود که به runComposeUiTest تغییر دهید.

رویکرد قبلی

استفاده از createComposeRule در ترکیب با runTest دو ساعت جداگانه ایجاد می‌کند: یکی برای Compose و دیگری برای محدوده روتین آزمایشی. این پیکربندی می‌تواند شما را مجبور کند زمان‌بندی آزمایش را به‌صورت دستی همگام‌سازی کنید.

@get:Rule
val composeTestRule = createComposeRule()

@Test
fun testWithCoroutines() {
    composeTestRule.setContent {
        var status by remember { mutableStateOf("Loading...") }
        LaunchedEffect(Unit) {
            delay(1000)
            status = "Done!"
        }
        Text(text = status)
    }

    // NOT RECOMMENDED
    // Fails: runTest creates a new, separate scheduler.
    // Advancing time here does NOT advance the compose clock.
    // To fix this without migrating, you would need to share the scheduler
    // by passing 'composeTestRule.mainClock.scheduler' to runTest.
    runTest {
        composeTestRule.onNodeWithText("Loading...").assertIsDisplayed()
        advanceTimeBy(1000)
        composeTestRule.onNodeWithText("Done!").assertIsDisplayed()
    }
}

‫runComposeUiTest API به‌طور خودکار بلوک آزمایش شما را در محدوده runTest خود اجرا می‌کند. ساعت آزمایشی با محیط «نوشتن» همگام‌سازی می‌شود، بنابراین دیگر نیازی نیست زمان‌بند را به‌صورت دستی مدیریت کنید.

    @Test
    fun testWithCoroutines() = runComposeUiTest {
        setContent {
            var status by remember { mutableStateOf("Loading...") }
            LaunchedEffect(Unit) {
                delay(1000)
                status = "Done!"
            }
            Text(text = status)
        }

        onNodeWithText("Loading...").assertIsDisplayed()
        mainClock.advanceTimeBy(1000 + 16 /* Frame buffer */)
        onNodeWithText("Done!").assertIsDisplayed()
    }
}