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

يتكامل 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 في قاعدة الاختبار. تقبل هذه الدالة 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).

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