اختبار الأجزاء غير النشطة

يوضّح هذا الموضوع كيفية تضمين واجهات برمجة التطبيقات التي يقدّمها إطار العمل في الاختبارات التي تقيّم سلوك كل جزء.

تعمل الأجزاء كحاويات قابلة لإعادة الاستخدام داخل تطبيقك، مما يسمح لك بتقديم نفس تخطيط واجهة المستخدم في مجموعة متنوعة من الأنشطة وعمليات تهيئة التخطيط. نظرًا لتنوع الأجزاء، من المهم التأكد من أنّها تقدّم تجربة متّسقة من حيث توفير الموارد. لاحظ ما يلي:

  • ويجب ألا يعتمد الجزء على نشاط رئيسي أو جزء محدّد منه.
  • يجب عدم إنشاء العرض الهرمي لطريقة عرض الجزء ما لم يكن الجزء مرئيًا للمستخدم.

للمساعدة في إعداد شروط إجراء هذه الاختبارات، توفّر مكتبة fragment-testing على AndroidX صف FragmentScenario لإنشاء أجزاء وتغيير Lifecycle.State.

تعريف التبعيات

لاستخدام FragmentScenario، حدِّد العنصر fragment-testing في ملف build.gradle لتطبيقك باستخدام debugImplementation، كما هو موضّح في المثال التالي:

رائع

dependencies {
    def fragment_version = "1.6.2"

    debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
}

Kotlin

dependencies {
    val fragment_version = "1.6.2"

    debugImplementation("androidx.fragment:fragment-testing:$fragment_version")
}

تستخدم الأمثلة التجريبية في هذه الصفحة تأكيدات من مكتبتَي Espresso وTruth. للحصول على معلومات عن مكتبات الاختبار والتأكيد الأخرى المتاحة، يمكنك الاطّلاع على إعداد المشروع لتطبيق AndroidX Test.

إنشاء جزء

يتضمّن FragmentScenario الطرق التالية لتشغيل الأجزاء في الاختبارات:

  • launchInContainer()، لاختبار واجهة المستخدم لجزء معيّن. ترفق FragmentScenario الجزء بوحدة التحكم في العرض الجذر للنشاط. وهذا العنصر الذي يحتوي على النشاط فارغ.
  • launch()، للاختبار بدون واجهة مستخدم الجزء يرفِق FragmentScenario هذا النوع من الأجزاء بنشاط فارغ، والذي لا يتضمّن عرض جذر.

بعد تشغيل أحد أنواع الأجزاء هذه، يقود FragmentScenario الجزء الذي يخضع للاختبار إلى حالة محدّدة. تكون هذه الحالة RESUMED تلقائيًا، ولكن يمكنك تجاوزها باستخدام الوسيطة initialState. تشير حالة RESUMED إلى أنّ الجزء قيد التشغيل ومرئي للمستخدم. يمكنك تقييم معلومات حول عناصر واجهة المستخدم الخاصة بها باستخدام اختبارات Espresso UI.

توضّح أمثلة الرموز التالية كيفية تشغيل الجزء باستخدام كل طريقة:

مثال على launchInContainer()

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        // The "fragmentArgs" argument is optional.
        val fragmentArgs = bundleOf(“selectedListItem” to 0)
        val scenario = launchFragmentInContainer<EventFragment>(fragmentArgs)
        ...
    }
}

مثال على launch()

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        // The "fragmentArgs" arguments are optional.
        val fragmentArgs = bundleOf("numElements" to 0)
        val scenario = launchFragment<EventFragment>(fragmentArgs)
        ...
    }
}

تقديم التبعيات

إذا كانت الأجزاء التابعة لك تتضمّن تبعيات، يمكنك تقديم إصدارات اختبارية لهذه الاعتماديات من خلال توفير سمة FragmentFactory مخصَّصة إلى طريقتَي launchInContainer() أو launch().

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val someDependency = TestDependency()
        launchFragmentInContainer {
            EventFragment(someDependency)
        }
        ...
    }
}

لمزيد من المعلومات حول استخدام FragmentFactory لتقديم تبعيات للأجزاء، راجع مدير الأجزاء.

تغيير حالة الجزء إلى حالة جديدة

عند إجراء اختبارات على واجهة المستخدم في التطبيق، يكفي عادةً تشغيل الجزء تحت الاختبار وبدء اختباره من حالة RESUMED. مع ذلك، في اختبارات الوحدات الأكثر دقة، يمكنك أيضًا تقييم سلوك الجزء أثناء انتقاله من حالة دورة حياة إلى أخرى. يمكنك تحديد الحالة الأولية من خلال تمرير الوسيطة initialState إلى أي من دوال launchFragment*().

لضبط الجزء إلى حالة مختلفة لمراحل النشاط، اطلب الرمز moveToState(). تتيح هذه الطريقة استخدام الحالات التالية كوسيطات: CREATED وSTARTED وRESUMED وDESTROYED. تحاكي هذه الطريقة حالة يغير فيها الجزء أو النشاط الذي يتضمّن الجزء الخاص بك حالته لأي سبب.

يبدأ المثال التالي بتشغيل جزء اختبار في حالة INITIALIZED ثم ينقله إلى حالة RESUMED:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>(
            initialState = Lifecycle.State.INITIALIZED
        )
        // EventFragment has gone through onAttach(), but not onCreate().
        // Verify the initial state.
        scenario.moveToState(Lifecycle.State.RESUMED)
        // EventFragment moves to CREATED -> STARTED -> RESUMED.
        ...
    }
}

إعادة إنشاء الجزء

إذا كان تطبيقك يعمل على جهاز منخفض الموارد، قد يدمر النظام النشاط الذي يحتوي على الجزء. ويتطلب هذا الموقف من تطبيقك إعادة إنشاء الجزء عندما يعود المستخدم إليه. لمحاكاة هذا الموقف، اتصل بـ recreate():

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        scenario.recreate()
        ...
    }
}

يؤدي استخدام FragmentScenario.recreate() إلى تدمير الجزء ومضيفه، ثم إعادة إنشاءهما. عندما تعيد الفئة FragmentScenario إنشاء الجزء الذي خضع للاختبار، يعود الجزء إلى الحالة التي كان عليها قبل تلفه.

التفاعل مع أجزاء واجهة المستخدم

لتشغيل إجراءات واجهة المستخدم في الجزء قيد الاختبار، استخدِم مطابقة طريقة عرض Espresso للتفاعل مع العناصر في طريقة العرض:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        onView(withId(R.id.refresh)).perform(click())
        // Assert some expected behavior
        ...
    }
}

إذا احتجت إلى استدعاء طريقة على الجزء نفسه، مثل الاستجابة لجزء محدّد في قائمة الخيارات، يمكنك إجراء ذلك بأمان من خلال الحصول على مرجع للجزء باستخدام FragmentScenario.onFragment() وتمرير علامة FragmentAction:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        scenario.onFragment { fragment ->
            fragment.myInstanceMethod()
        }
    }
}

إجراءات مربّع الحوار التجريبية

يتيح FragmentScenario أيضًا اختبار أجزاء مربّع الحوار. على الرغم من أنّ أجزاء مربّع الحوار تحتوي على عناصر في واجهة المستخدم، تتم تعبئة تنسيقها في نافذة منفصلة، وليس في النشاط نفسه. ولهذا السبب، يمكنك استخدام FragmentScenario.launch() لاختبار أجزاء مربّع الحوار.

يختبر المثال التالي عملية إغلاق مربع الحوار:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testDismissDialogFragment() {
        // Assumes that "MyDialogFragment" extends the DialogFragment class.
        with(launchFragment<MyDialogFragment>()) {
            onFragment { fragment ->
                assertThat(fragment.dialog).isNotNull()
                assertThat(fragment.requireDialog().isShowing).isTrue()
                fragment.dismiss()
                fragment.parentFragmentManager.executePendingTransactions()
                assertThat(fragment.dialog).isNull()
            }
        }

        // Assumes that the dialog had a button
        // containing the text "Cancel".
        onView(withText("Cancel")).check(doesNotExist())
    }
}