إمكانية التشغيل التفاعلي

تتكامل Compose مع أُطر الاختبار الشائعة.

إمكانية التشغيل التفاعلي مع Espresso

في التطبيق المختلط، يمكنك العثور على مكوّنات Compose داخل تسلسلات هرمية للعناصر المرئية، وعلى عناصر مرئية داخل دوال Compose المركّبة (من خلال الدالة المركّبة AndroidView).

ليست هناك خطوات خاصة يجب اتّخاذها لمطابقة أيّ من النوعين. يمكنك مطابقة طرق العرض مع 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()))
}

إضافة دلالات على مستوى العرض لاختبار إمكانية التشغيل التفاعلي مع Compose

حصر عمليات البحث في Compose على طرق عرض معيّنة

عند نقل واجهات مستخدم معقّدة إلى Compose، قد تواجه عناصر Compose متطابقة مضمّنة داخل عدة طرق عرض تقليدية في Android، مثل RecyclerView أو ViewPager. في هذه السيناريوهات، قد يتعذّر إجراء بحث عادي في Compose، مثل onNodeWithText("Save")، ويظهر الخطأ "تم العثور على عُقد متعددة".

بدلاً من تعديل رمز الإنتاج لإدخال علامات اختبار ديناميكية من أجل التمييز بين هذه العناصر، يمكنك تحديد نطاق اختبار Compose مباشرةً إلى Android View معيّن.

استخدِم واجهة برمجة التطبيقات onRootWithViewInteraction في قاعدة الاختبار. تقبل هذه الدالة ViewInteraction من Espresso، ما يتيح لك الاستفادة من Espresso لعزل View حاوية معيّنة وتنفيذ تفاعلات 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()
}

حلّ الغموض في ViewPager

عندما تكون هناك عدة أجزاء بتنسيقات 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).

فعِّل السمة الدلالية مرة واحدة فقط في أعلى تسلسل هرمي للعناصر القابلة للإنشاء لضمان إمكانية الوصول إلى جميع العناصر القابلة للإنشاء المتداخلة التي تتضمّن 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.

موارد إضافية

  • اختبار التطبيقات على Android: تقدّم الصفحة المقصودة الرئيسية لاختبار Android نظرة أوسع على أساسيات الاختبار وأساليبه.
  • أساسيات الاختبار: مزيد من المعلومات عن المفاهيم الأساسية لاختبار تطبيق Android
  • الاختبارات المحلية: يمكنك إجراء بعض الاختبارات محليًا على محطة العمل الخاصة بك.
  • اختبارات لقياس حالة التطبيق: من الممارسات الجيدة أيضًا إجراء اختبارات لقياس حالة التطبيق. أي الاختبارات التي يتم إجراؤها مباشرةً على الجهاز.
  • التكامل المستمر: يتيح لك التكامل المستمر دمج اختباراتك في مسار النشر.
  • اختبار أحجام الشاشات المختلفة: بما أنّ المستخدمين يتوفّر لديهم العديد من الأجهزة، عليك اختبار أحجام الشاشات المختلفة.
  • Espresso: على الرغم من أنّ Espresso مُصمَّمة لواجهات المستخدم المستندة إلى العرض، إلا أنّ معرفة أدوات Espresso يمكن أن تكون مفيدة في بعض جوانب اختبار Compose.