من مزايا استخدام أُطر عمل لتوفير التبعية مثل Hilt أنّها تسهّل اختبار الرمز البرمجي.
اختبارات الوحدات
لا حاجة إلى استخدام Hilt في اختبارات الوحدة، لأنّه عند اختبار فئة تستخدم ميزة الحقن عبر المنشئ، لن تحتاج إلى استخدام Hilt لإنشاء مثيل لهذه الفئة. بدلاً من ذلك، يمكنك استدعاء أداة إنشاء فئة مباشرةً من خلال تمرير تبعيات وهمية أو محاكاة، تمامًا كما تفعل إذا لم يتم وضع تعليق توضيحي على أداة الإنشاء:
@ActivityScoped class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... } class AnalyticsAdapterTest { @Test fun `Happy path`() { // You don't need Hilt to create an instance of AnalyticsAdapter. // You can pass a fake or mock AnalyticsService. val adapter = AnalyticsAdapter(fakeAnalyticsService) assertEquals(...) } }
وينطبق الأمر نفسه على فئات ViewModel التي يتم الحصول عليها من خلال استدعاء hiltViewModel() في عناصرك القابلة للإنشاء. في اختبارات الوحدة، أنشئ ViewModel مباشرةً باستخدام عناصر وهمية.
للحصول على معلومات حول كيفية انتقال الحالة من ViewModel إلى العناصر القابلة للإنشاء، يُرجى الاطّلاع على الحالة وJetpack Compose وموضع نقل الحالة.
اختبارات شاملة
بالنسبة إلى اختبارات الدمج، يحقن Hilt التبعيات كما يفعل في رمز الإنتاج. لا يتطلّب الاختبار باستخدام Hilt أي صيانة لأنّ Hilt ينشئ تلقائيًا مجموعة جديدة من المكوّنات لكل اختبار.
إضافة تبعيات الاختبار
لاستخدام Hilt في اختباراتك، أدرِج التبعية hilt-android-testing في مشروعك:
dependencies { // For Robolectric tests. testImplementation("com.google.dagger:hilt-android-testing:2.57.1") kspTest("com.google.dagger:hilt-android-compiler:2.57.1") // For instrumented tests. androidTestImplementation("com.google.dagger:hilt-android-testing:2.57.1") kspAndroidTest("com.google.dagger:hilt-android-compiler:2.57.1") // Compose UI test rule. androidTestImplementation("androidx.compose.ui:ui-test-junit4") debugImplementation("androidx.compose.ui:ui-test-manifest") }
إعداد اختبار واجهة المستخدم
يجب إضافة التعليق التوضيحي @HiltAndroidTest إلى أي اختبار لواجهة المستخدم يستخدم Hilt. هذه التعليق التوضيحي مسؤول عن إنشاء مكوّنات Hilt لكل اختبار.
عليك أيضًا إضافة HiltAndroidRule إلى فئة الاختبار. تتولّى هذه السمة إدارة حالة المكوّنات، ويتم استخدامها لتنفيذ عملية الإدخال في الاختبار:
@HiltAndroidTest class SettingsScreenTest { @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this) @get:Rule(order = 1) val composeRule = createAndroidComposeRule<HiltTestActivity>() // Compose UI tests here. }
بعد ذلك، يجب أن يعرف اختبارك الفئة Application التي ينشئها Hilt تلقائيًا.
للسماح لمكتبة Hilt بإدخال التبعيات، عليك إنشاء نشاط فارغ باسم
HiltTestActivity في مجموعة رموز المصدر androidTest وإضافة التعليق التوضيحي
@AndroidEntryPoint إليه. تستخدم createAndroidComposeRule بعد ذلك هذا النشاط كمضيف للمحتوى القابل للإنشاء.
تطبيق الاختبار
يجب تنفيذ اختبارات مزوَّدة بأدوات تستخدم Hilt في عنصر Application يتوافق مع Hilt. توفّر المكتبة HiltTestApplication لاستخدامها في الاختبارات.
إذا كانت اختباراتك تتطلّب تطبيقًا أساسيًا مختلفًا، يمكنك الاطّلاع على تطبيق مخصّص للاختبارات.
يجب ضبط تطبيق الاختبار على التشغيل في الاختبارات المزوّدة بأدوات أو اختبارات Robolectric. التعليمات التالية ليست خاصة بـ Hilt، ولكنّها إرشادات عامة حول كيفية تحديد تطبيق مخصّص لتشغيله في الاختبارات.
ضبط التطبيق التجريبي في الاختبارات المبرمَجة
لاستخدام تطبيق اختبار Hilt في الاختبارات المبرمَجة، عليك ضبط أداة تشغيل اختبار جديدة. يؤدي ذلك إلى إتاحة استخدام Hilt لجميع الاختبارات المزوّدة بأدوات في مشروعك. اتّبِع الخطوات التالية:
- أنشئ فئة مخصّصة توسّع
AndroidJUnitRunnerفي المجلدandroidTest. - تجاوز الدالة
newApplicationوأدخِل اسم تطبيق اختبار Hilt الذي تم إنشاؤه.
// A custom runner to set up the instrumented application class for tests. class CustomTestRunner : AndroidJUnitRunner() { override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application { return super.newApplication(cl, HiltTestApplication::class.java.name, context) } }
بعد ذلك، اضبط أداة تشغيل الاختبار هذه في ملف Gradle كما هو موضّح في دليل اختبارات الوحدات المبرمَجة. تأكَّد من استخدام مسار الفئة الكامل:
android { defaultConfig { // Replace com.example.android.dagger with your class path. testInstrumentationRunner = "com.example.android.dagger.CustomTestRunner" } }
ضبط التطبيق التجريبي في اختبارات Robolectric
إذا كنت تستخدم Robolectric لاختبار طبقة واجهة المستخدم، يمكنك تحديد التطبيق الذي تريد استخدامه في ملف robolectric.properties:
application = dagger.hilt.android.testing.HiltTestApplication
بدلاً من ذلك، يمكنك ضبط إعدادات التطبيق في كل اختبار على حدة باستخدام التعليق التوضيحي @Config في Robolectric:
@HiltAndroidTest @Config(application = HiltTestApplication::class) class SettingsScreenTest { @get:Rule var hiltRule = HiltAndroidRule(this) // Robolectric tests here. }
ميزات الاختبار
بعد أن يصبح Hilt جاهزًا للاستخدام في اختباراتك، يمكنك استخدام العديد من الميزات لتخصيص عملية الاختبار.
إدخال أنواع في الاختبارات
لإدخال أنواع في اختبار، استخدِم @Inject للحقن المباشر في المتغيرات. لإخبار Hilt بملء حقول @Inject، استخدِم الدالة hiltRule.inject().
في ما يلي مثال على اختبار مزوَّد بأدوات:
@HiltAndroidTest class SettingsScreenTest { @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this) @get:Rule(order = 1) val composeRule = createAndroidComposeRule<HiltTestActivity>() @Inject lateinit var analyticsAdapter: AnalyticsAdapter @Before fun init() { hiltRule.inject() } @Test fun settingsScreen_showsTitle() { composeRule.setContent { SettingsScreen() } composeRule.onNodeWithText("Settings").assertIsDisplayed() // analyticsRepository is available here. } }
استبدال عملية ربط
إذا كنت بحاجة إلى إدخال مثيل زائف أو وهمي لاعتمادية، عليك إخبار Hilt بعدم استخدام الربط الذي استخدمته في رمز الإنتاج واستخدام ربط مختلف بدلاً منه. لاستبدال رابط، عليك استبدال الوحدة التي تحتوي على الرابط بوحدة اختبار تحتوي على الروابط التي تريد استخدامها في الاختبار.
على سبيل المثال، لنفترض أنّ الرمز البرمجي الخاص بالإنتاج يعرّف ربطًا لـ
AnalyticsService على النحو التالي:
@Module @InstallIn(SingletonComponent::class) abstract class AnalyticsModule { @Singleton @Binds abstract fun bindAnalyticsService( analyticsServiceImpl: AnalyticsServiceImpl ): AnalyticsService }
لاستبدال عملية الربط AnalyticsService في الاختبارات، أنشئ وحدة Hilt جديدة في المجلد test أو androidTest باستخدام الاعتمادية الوهمية، وأضِف إليها التعليق التوضيحي @TestInstallIn. بدلاً من ذلك، يتم إدخال جميع الاختبارات في هذا المجلد مع التبعية الوهمية.
@Module @TestInstallIn( components = [SingletonComponent::class], replaces = [AnalyticsModule::class] ) abstract class FakeAnalyticsModule { @Singleton @Binds abstract fun bindAnalyticsService( fakeAnalyticsService: FakeAnalyticsService ): AnalyticsService }
وبما أنّ العناصر القابلة للإنشاء تستهلك عادةً هذه التبعيات بشكل غير مباشر من خلال
ViewModel يتم الحصول عليه باستخدام hiltViewModel()، يكفي استبدال الربط في Hilt. يتم تلقائيًا استلام العنصر القابل للإنشاء الذي يتم اختباره.
استبدال ربط في اختبار واحد
لاستبدال ربط في اختبار واحد بدلاً من جميع الاختبارات، عليك إلغاء تثبيت وحدة Hilt من اختبار باستخدام التعليق التوضيحي @UninstallModules وإنشاء وحدة اختبار جديدة داخل الاختبار.
باتّباع مثال AnalyticsService من الإصدار السابق، ابدأ بإخبار Hilt بتجاهل وحدة الإنتاج باستخدام التعليق التوضيحي @UninstallModules في فئة الاختبار:
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsScreenTest { ... }
بعد ذلك، عليك استبدال عملية الربط. أنشئ وحدة جديدة ضمن فئة الاختبار تحدّد ربط الاختبار:
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsScreenTest { @Module @InstallIn(SingletonComponent::class) abstract class TestModule { @Singleton @Binds abstract fun bindAnalyticsService( fakeAnalyticsService: FakeAnalyticsService ): AnalyticsService } // ... }
يؤدي ذلك إلى استبدال عملية الربط لفئة اختبار واحدة فقط. إذا أردت استبدال عملية الربط لجميع فئات الاختبار، استخدِم التعليق التوضيحي @TestInstallIn من القسم أعلاه. بدلاً من ذلك، يمكنك وضع أداة ربط الاختبار في الوحدة test لاختبارات Robolectric، أو في الوحدة androidTest للاختبارات المبرمَجة.
ننصح باستخدام @TestInstallIn كلما أمكن ذلك.
ربط القيم الجديدة
استخدِم التعليق التوضيحي @BindValue لربط الحقول في اختبارك بسهولة بمخطط بيانات التبعية في Hilt. أضِف التعليق التوضيحي @BindValue إلى حقل، وسيتم ربطه بنوع الحقل المعلَن عنه مع أي مؤهلات متوفرة لهذا الحقل.
في مثال AnalyticsService، يمكنك استبدال AnalyticsService بنص
زائف باستخدام @BindValue:
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsScreenTest { @BindValue @JvmField val analyticsService: AnalyticsService = FakeAnalyticsService() ... }
يؤدي ذلك إلى تبسيط عملية استبدال رابط وعملية الإشارة إلى رابط في الاختبار من خلال السماح لك بإجراء كلتا العمليتين في الوقت نفسه.
تعمل @BindValue مع المؤهّلات والتعليقات التوضيحية الأخرى للاختبار. على سبيل المثال، إذا كنت تستخدم مكتبات اختبار مثل Mockito، يمكنك استخدامها في اختبار Robolectric على النحو التالي:
... class SettingsScreenTest { ... @BindValue @ExampleQualifier @Mock lateinit var qualifiedVariable: ExampleCustomType // Robolectric tests here }
إذا كنت بحاجة إلى إضافة ربط متعدّد، يمكنك استخدام التعليقَين التوضيحيَّين @BindValueIntoSet و@BindValueIntoMap بدلاً من @BindValue. يتطلّب @BindValueIntoMap منك أيضًا إضافة تعليق توضيحي إلى الحقل
باستخدام تعليق توضيحي لمفتاح الخريطة.
حالات خاصة
توفّر Hilt أيضًا ميزات لدعم حالات الاستخدام غير العادية.
تطبيق مخصّص للاختبارات
إذا تعذّر عليك استخدام HiltTestApplication لأنّ تطبيقك التجريبي يحتاج إلى توسيع تطبيق آخر، يمكنك إضافة تعليق توضيحي إلى فئة أو واجهة جديدة باستخدام @CustomTestApplication، مع تمرير قيمة الصنف الأساسي التي تريد أن يوسّعها تطبيق Hilt الذي تم إنشاؤه.
ستنشئ @CustomTestApplication فئة Application جاهزة للاختبار
باستخدام Hilt، وتوسّع هذه الفئة التطبيق الذي مرّرته كمَعلمة.
@CustomTestApplication(BaseApplication::class) interface HiltTestApplication
في المثال، ينشئ Hilt فئة Application باسم
HiltTestApplication_Application توسّع الفئة BaseApplication. بشكل عام، يكون اسم التطبيق الذي تم إنشاؤه هو اسم الفئة التي تمّت إضافة التعليقات التوضيحية إليها، متبوعًا بـ _Application. يجب ضبط تطبيق الاختبار الذي تم إنشاؤه باستخدام Hilt ليتم تشغيله في الاختبارات التي تتطلّب أجهزة أو اختبارات Robolectric كما هو موضّح في تطبيق الاختبار.
عناصر TestRule المتعددة في اختبارك المبرمَج
تجمع اختبارات واجهة مستخدم Compose حاليًا بين HiltAndroidRule وقاعدة اختبار Compose، مثل createAndroidComposeRule. إذا كان لديك عناصر TestRule إضافية، تأكَّد من تشغيل HiltAndroidRule أولاً. حدِّد ترتيب التنفيذ باستخدام السمة order في @Rule:
@HiltAndroidTest class SettingsScreenTest { @get:Rule(order = 0) var hiltRule = HiltAndroidRule(this) @get:Rule(order = 1) val composeRule = createAndroidComposeRule<HiltTestActivity>() @get:Rule(order = 2) val otherRule = SomeOtherRule() // UI tests here. }
يمكنك بدلاً من ذلك تضمين القواعد في RuleChain، مع وضع HiltAndroidRule كقاعدة خارجية.
@HiltAndroidTest class SettingsScreenTest { @get:Rule var rule = RuleChain.outerRule(HiltAndroidRule(this)). around(SettingsScreenTestRule(...)) // UI tests here. }
استخدام نقطة دخول قبل توفّر المكوّن الفردي
توفّر العلامة التوضيحية @EarlyEntryPoint طريقة للحلّ عند الحاجة إلى إنشاء نقطة دخول في Hilt قبل أن يتوفّر مكوّن سينغلتون في اختبار Hilt.
يمكنك الاطّلاع على مزيد من المعلومات حول @EarlyEntryPoint في
مستندات Hilt.
مراجع إضافية
لمزيد من المعلومات حول الاختبار، يُرجى الاطّلاع على المراجع الإضافية التالية: