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

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

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

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

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

میاناهای برنامه‌سازی کاربردی Compose test v2 از ComposeUiTestConfig برای سفارشی‌سازی محیط آزمون استفاده می‌کنند. میاناهای برنامه‌سازی کاربردی که عملکردهای راه‌اندازی را برای آزمایش‌ها ایجاد می‌کنند، مانند createComposeRule، runComposeUiTest، و دیگر میاناهای برنامه‌سازی کاربردی مرتبط، ComposeUiTestConfig را می‌پذیرند. این شیء پیکربندی، میاناهای برنامه‌سازی کاربردی مرتبط با محیط مانند effectContext،‏ runTestContext، و testTimeout را در یک شیء واحد ادغام می‌کند.

مدل پیکربندی inputMode را نیز مدیریت می‌کند. Compose test v2 APIs enforce InputMode.Touch by default at the start of each test to ensure determinism and prevent input mode state from leaking between tests.

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

انتقال به 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
    }
}

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

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

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

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

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

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

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 ایجاد می‌کنید و آن سازنده را فراخوانی می‌کنید.

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

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

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

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

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

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

رویکرد قبلی

در نسخه ۱، تکلیف بلافاصله راه‌اندازی و تمام شد. در نسخه ۲، کد زیر ناموفق است چون 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() استفاده کنید. این کار تکالیف زمان‌بندی‌شده برای زمان مجازی فعلی را اجرا می‌کند.

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 اجرا می‌کند. ساعت آزمایشی با محیط Compose همگام‌سازی می‌شود، بنابراین دیگر نیازی به مدیریت دستی زمان‌بند ندارید.

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