قابلیت همکاری

Compose با چارچوب‌های تست رایج ادغام می‌شود.

قابلیت همکاری با اسپرسو

در یک برنامه ترکیبی، می‌توانید کامپوننت‌های Compose را درون سلسله مراتب view و viewها را درون Composableهای Compose (از طریق AndroidView composable) پیدا کنید.

برای تطبیق هیچ‌کدام از این دو نوع، نیازی به انجام مراحل خاصی نیست. شما نماها را با onView در Espresso و عناصر Compose را با ComposeTestRule تطبیق می‌دهید.

@Test
fun androidViewInteropTest() {
    // Check the initial state of a TextView that depends on a Compose state.
    Espresso.onView(withText("Hello Views")).check(matches(isDisplayed()))
    // Click on the Compose button that changes the state.
    composeTestRule.onNodeWithText("Click here").performClick()
    // Check the new value.
    Espresso.onView(withText("Hello Compose")).check(matches(isDisplayed()))
}

افزودن معناشناسیِ مبتنی بر View برای تست تعامل Compose

دامنه جستجوهای نوشتن به نماهای خاص

هنگام انتقال رابط‌های کاربری پیچیده به Compose، ممکن است با عناصر Compose یکسانی که درون چندین View سنتی اندروید - مانند یک RecyclerView یا یک ViewPager - تو در تو قرار گرفته‌اند، مواجه شوید. در این سناریوها، یک جستجوی استاندارد Compose مانند onNodeWithText("Save") ممکن است با خطای "چندین گره یافت شد" شکست بخورد.

به جای تغییر کد عملیاتی خود برای تزریق تگ‌های تست پویا برای تمایز این عناصر، می‌توانید تست Compose خود را مستقیماً به یک نمای خاص اندروید محدود کنید.

از API onRootWithViewInteraction روی قانون تست خود استفاده کنید. این تابع یک Espresso ViewInteraction می‌پذیرد که به شما امکان می‌دهد از Espresso برای جداسازی یک نمای کانتینر خاص استفاده کنید و تعاملات Compose را منحصراً در آن سلسله مراتب محدود انجام دهید.

تعامل با یک آیتم لیست

اگر نیاز دارید که با یک عنصر Compose درون یک ردیف خاص RecyclerView تعامل داشته باشید، از Espresso برای یافتن ردیف مورد نظر استفاده کنید، سپس دامنه تعامل Compose خود را به آن محدود کنید. این کار عناصر Compose مشابه را در تمام ردیف‌های دیگر نادیده می‌گیرد.

@Test
fun testComposeButtonInsideRecyclerViewItem() = runComposeUiTest {
    // Scroll to the desired position using Espresso
    Espresso.onView(withId(recyclerViewId))
        .perform(RecyclerViewActions.scrollToPosition<MyViewHolder>(3))

    // Define an Espresso ViewInteraction that uniquely identifies the row
    val rowView = Espresso.onView(
        allOf(
            withId(rootViewId),
            hasDescendant(withText("Item #3"))
        )
    )

    // Scope the Compose search strictly to that specific row View
    onRootWithViewInteraction(rowView)
        .onNode(hasText("Like"))
        .performClick()
}

رفع ابهام در ViewPagers

وقتی چندین فرگمنت با طرح‌بندی‌های Compose یکسان به طور همزمان در حافظه هستند، می‌توانید جستجو را به شناسه نمای ریشه فرگمنت خاص محدود کنید تا از ابهام در تطبیق جلوگیری شود.

@Test
fun testComposeButtonInsideViewPagerItem() = runComposeUiTest {
    // Swipe to the desired page using Espresso
    Espresso.onView(withId(viewPagerViewId)).perform(swipeLeft())

    // Identify the specific container view using Espresso
    val fragmentB = Espresso.onView(withId(fragmentRootViewId))

    // The generic text "Save" is now unique within this view scope
    onRootWithViewInteraction(fragmentB)
        .onNode(hasText("Save"))
        .assertIsDisplayed()
}

قابلیت همکاری با UIAutomator

به طور پیش‌فرض، کامپوننت‌ها فقط از طریق توصیف‌گرهای مناسب خود (متن نمایش داده شده، توضیحات محتوا و غیره) از UiAutomator قابل دسترسی هستند. اگر می‌خواهید به هر کامپوننتی که از Modifier.testTag استفاده می‌کند دسترسی داشته باشید، باید ویژگی معنایی testTagsAsResourceId برای زیردرخت کامپوننت خاص فعال کنید. فعال کردن این رفتار برای کامپوننت‌هایی که هیچ دسته منحصر به فرد دیگری ندارند، مانند کامپوننت‌های اسکرول شونده (مثلاً LazyColumn )، مفید است.

ویژگی semantic را فقط یک بار در سلسله مراتب composables خود فعال کنید تا مطمئن شوید که همه composables های تو در تو با Modifier.testTag از UiAutomator قابل دسترسی هستند.

Scaffold(
    // Enables for all composables in the hierarchy.
    modifier = Modifier.semantics {
        testTagsAsResourceId = true
    }
){
    // Modifier.testTag is accessible from UiAutomator for composables nested here.
    LazyColumn(
        modifier = Modifier.testTag("myLazyColumn")
    ){
        // Content
    }
}

هر ترکیب‌پذیری با Modifier.testTag(tag) می‌تواند با استفاده از By.res(resourceName) با استفاده از همان tag resourceName قابل دسترسی باشد.

val device = UiDevice.getInstance(getInstrumentation())

val lazyColumn: UiObject2 = device.findObject(By.res("myLazyColumn"))
// Some interaction with the lazyColumn.

منابع اضافی

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