المفاهيم وتنفيذ Jetpack Compose
من مزايا استخدام أُطر عمل حقن التبعية مثل Hilt أنّها تسهّل اختبار الرمز البرمجي.
اختبارات الوحدات
لا يُعدّ Hilt ضروريًا لاختبارات الوحدة، لأنّه عند اختبار فئة تستخدم الحقن عبر المنشئ، لن تحتاج إلى استخدام Hilt لإنشاء مثيل لتلك الفئة. بدلاً من ذلك، يمكنك استدعاء دالة إنشاء الصف مباشرةً من خلال تمرير تبعيات وهمية أو زائفة، تمامًا كما تفعل إذا لم تكن دالة الإنشاء مشروحة:
Kotlin
@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(...) } }
Java
@ActivityScope public class AnalyticsAdapter { private final AnalyticsService analyticsService; @Inject AnalyticsAdapter(AnalyticsService analyticsService) { this.analyticsService = analyticsService; } } public final class AnalyticsAdapterTest { @Test public void happyPath() { // You don't need Hilt to create an instance of AnalyticsAdapter. // You can pass a fake or mock AnalyticsService. AnalyticsAdapter adapter = new AnalyticsAdapter(fakeAnalyticsService); assertEquals(...); } }
الاختبارات الشاملة
بالنسبة إلى اختبارات التكامل، يحقن Hilt التبعيات كما يفعل في رمز الإنتاج. لا يتطلّب الاختبار باستخدام Hilt أي صيانة لأنّ Hilt ينشئ تلقائيًا مجموعة جديدة من المكوّنات لكل اختبار.
إضافة تبعيات الاختبار
لاستخدام Hilt في اختباراتك، أدرِج اعتمادية hilt-android-testing في مشروعك:
أنيق
dependencies { // For Robolectric tests. testImplementation 'com.google.dagger:hilt-android-testing:2.57.1' // ...with Kotlin. kaptTest 'com.google.dagger:hilt-android-compiler:2.57.1' // ...with Java. testAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.57.1' // For instrumented tests. androidTestImplementation 'com.google.dagger:hilt-android-testing:2.57.1' // ...with Kotlin. kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.57.1' // ...with Java. androidTestAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.57.1' }
Kotlin
dependencies { // For Robolectric tests. testImplementation("com.google.dagger:hilt-android-testing:2.57.1") // ...with Kotlin. kaptTest("com.google.dagger:hilt-android-compiler:2.57.1") // ...with Java. testAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.57.1") // For instrumented tests. androidTestImplementation("com.google.dagger:hilt-android-testing:2.57.1") // ...with Kotlin. kaptAndroidTest("com.google.dagger:hilt-android-compiler:2.57.1") // ...with Java. androidTestAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.57.1") }
إعداد اختبار واجهة المستخدم
يجب إضافة تعليق توضيحي إلى أي اختبار لواجهة المستخدم يستخدم Hilt مع @HiltAndroidTest. هذا التعليق التوضيحي مسؤول عن إنشاء مكوّنات Hilt لكل اختبار.
عليك أيضًا إضافة HiltAndroidRule إلى فئة الاختبار. وتدير هذه الفئة حالة المكوّنات وتُستخدَم لإجراء عملية الحقن في الاختبار:
Kotlin
@HiltAndroidTest class SettingsActivityTest { @get:Rule var hiltRule = HiltAndroidRule(this) // UI tests here. }
Java
@HiltAndroidTest public final class SettingsActivityTest { @Rule public HiltAndroidRule hiltRule = new HiltAndroidRule(this); // UI tests here. }
بعد ذلك، يجب أن يعرف الاختبار عن فئة Application التي ينشئها Hilt
تلقائيًا لك.
تطبيق تجريبي
يجب تنفيذ اختبارات لقياس حالة التطبيق التي تستخدم Hilt في عنصر Application متوافق مع Hilt. توفّر المكتبة HiltTestApplication للاستخدام في الاختبارات.
إذا كانت اختباراتك تتطلّب تطبيقًا أساسيًا مختلفًا، يُرجى الاطّلاع على التطبيق المخصّص للاختبارات.
يجب ضبط تطبيق الاختبار ليتم تشغيله في الاختبارات المزوّدة بأدوات أو اختبارات Robolectric. التعليمات التالية ليست خاصة بـ Hilt، ولكنها إرشادات عامة حول كيفية تحديد تطبيق مخصّص لتشغيله في الاختبارات.
ضبط التطبيق التجريبي في الاختبارات الآلية
لاستخدام تطبيق اختبار Hilt في الاختبارات الآلية، عليك ضبط مشغّل اختبار جديد. يؤدي ذلك إلى تفعيل Hilt لجميع اختبارات قياس حالة التطبيق في مشروعك. اتّبِع الخطوات التالية:
- أنشئ فئة مخصّصة تمدّد
AndroidJUnitRunnerفي مجلدandroidTest. - تجاوز دالة
newApplicationومرِّر اسم تطبيق اختبار Hilt الذي تم إنشاؤه.
Kotlin
// 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) } }
Java
// A custom runner to set up the instrumented application class for tests. public final class CustomTestRunner extends AndroidJUnitRunner { @Override public Application newApplication(ClassLoader cl, String className, Context context) throws ClassNotFoundException, IllegalAccessException, InstantiationException { return super.newApplication(cl, HiltTestApplication.class.getName(), context); } }
بعد ذلك، اضبط أداة تشغيل الاختبار هذه في ملف Gradle كما هو موضّح في دليل اختبار الوحدة المزوَّد بأدوات. تأكَّد من استخدام مسار الفئة الكامل:
أنيق
android { defaultConfig { // Replace com.example.android.dagger with your class path. testInstrumentationRunner "com.example.android.dagger.CustomTestRunner" } }
Kotlin
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
بدلاً من ذلك، يمكنك ضبط التطبيق في كل اختبار على حدة باستخدام تعليق Robolectric التوضيحي @Config:
Kotlin
@HiltAndroidTest @Config(application = HiltTestApplication::class) class SettingsActivityTest { @get:Rule var hiltRule = HiltAndroidRule(this) // Robolectric tests here. }
Java
@HiltAndroidTest @Config(application = HiltTestApplication.class) class SettingsActivityTest { @Rule public HiltAndroidRule hiltRule = new HiltAndroidRule(this); // Robolectric tests here. }
إذا كنت تستخدم إصدارًا من المكوّن الإضافي لنظام Gradle المتوافق مع Android أقدم من 4.2، فعِّل
تحويل فئات @AndroidEntryPoint في اختبارات الوحدات المحلية من خلال تطبيق
الإعدادات التالية في ملف build.gradle الخاص بالوحدة:
أنيق
hilt { enableTransformForLocalTests = true }
Kotlin
hilt { enableTransformForLocalTests = true }
يمكنك الاطّلاع على مزيد من المعلومات حول enableTransformForLocalTests في مستندات Hilt.
ميزات الاختبار
بعد أن تصبح أداة Hilt جاهزة للاستخدام في الاختبارات، يمكنك استخدام عدة ميزات لتخصيص عملية الاختبار.
إدخال الأنواع في الاختبارات
لحقن الأنواع في اختبار، استخدِم @Inject للحقن المباشر في المتغيرات. لإعلام Hilt بملء حقول @Inject،
يمكنك استدعاء hiltRule.inject().
اطّلِع على المثال التالي لاختبار لقياس حالة التطبيق:
Kotlin
@HiltAndroidTest class SettingsActivityTest { @get:Rule var hiltRule = HiltAndroidRule(this) @Inject lateinit var analyticsAdapter: AnalyticsAdapter @Before fun init() { hiltRule.inject() } @Test fun `happy path`() { // Can already use analyticsAdapter here. } }
Java
@HiltAndroidTest public final class SettingsActivityTest { @Rule public HiltAndroidRule hiltRule = new HiltAndroidRule(this); @Inject AnalyticsAdapter analyticsAdapter; @Before public void init() { hiltRule.inject(); } @Test public void happyPath() { // Can already use analyticsAdapter here. } }
استبدال ربط
إذا كنت بحاجة إلى إدخال مثيل مزيّف أو وهمي من التبعية، عليك إخبار Hilt بعدم استخدام الربط الذي استخدمه في رمز الإنتاج واستخدام رمز مختلف بدلاً من ذلك. لاستبدال عملية ربط، عليك استبدال الوحدة التي تحتوي على عملية الربط بوحدة اختبار تحتوي على عمليات الربط التي تريد استخدامها في الاختبار.
على سبيل المثال، لنفترض أنّ رمز الإنتاج يعلن عن ربط لـ AnalyticsService على النحو التالي:
Kotlin
@Module @InstallIn(SingletonComponent::class) abstract class AnalyticsModule { @Singleton @Binds abstract fun bindAnalyticsService( analyticsServiceImpl: AnalyticsServiceImpl ): AnalyticsService }
Java
@Module @InstallIn(SingletonComponent.class) public abstract class AnalyticsModule { @Singleton @Binds public abstract AnalyticsService bindAnalyticsService( AnalyticsServiceImpl analyticsServiceImpl ); }
لاستبدال الربط "AnalyticsService" في الاختبارات، أنشِئ وحدة Hilt جديدة في
المجلد "test" أو "androidTest" باستخدام التبعية الزائفة وأضِف تعليقًا توضيحيًا إليها
باستخدام "@TestInstallIn". يتم حقن جميع الاختبارات في هذا المجلد بالتبعية
المزيفة بدلاً من ذلك.
Kotlin
@Module @TestInstallIn( components = [SingletonComponent::class], replaces = [AnalyticsModule::class] ) abstract class FakeAnalyticsModule { @Singleton @Binds abstract fun bindAnalyticsService( fakeAnalyticsService: FakeAnalyticsService ): AnalyticsService }
Java
@Module @TestInstallIn( components = SingletonComponent.class, replaces = AnalyticsModule.class ) public abstract class FakeAnalyticsModule { @Singleton @Binds public abstract AnalyticsService bindAnalyticsService( FakeAnalyticsService fakeAnalyticsService ); }
استبدال عملية ربط في اختبار واحد
لاستبدال عملية ربط في اختبار واحد بدلاً من جميع الاختبارات، يمكنك إلغاء تثبيت وحدة Hilt من اختبار باستخدام التعليق التوضيحي @UninstallModules وإنشاء وحدة اختبار جديدة داخل الاختبار.
باتّباع مثال AnalyticsService من الإصدار السابق، ابدأ بإخبار Hilt بتجاهُل وحدة الإنتاج باستخدام التعليق التوضيحي @UninstallModules في فئة الاختبار:
Kotlin
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsActivityTest { ... }
Java
@UninstallModules(AnalyticsModule.class) @HiltAndroidTest public final class SettingsActivityTest { ... }
بعد ذلك، عليك استبدال الربط. أنشئ وحدة جديدة داخل فئة الاختبار تحدّد عملية الربط التجريبية:
Kotlin
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsActivityTest { @Module @InstallIn(SingletonComponent::class) abstract class TestModule { @Singleton @Binds abstract fun bindAnalyticsService( fakeAnalyticsService: FakeAnalyticsService ): AnalyticsService } ... }
Java
@UninstallModules(AnalyticsModule.class) @HiltAndroidTest public final class SettingsActivityTest { @Module @InstallIn(SingletonComponent.class) public abstract class TestModule { @Singleton @Binds public abstract AnalyticsService bindAnalyticsService( FakeAnalyticsService fakeAnalyticsService ); } ... }
يؤدي هذا إلى استبدال الربط لفئة اختبار واحدة فقط. إذا أردت استبدال
الربط لجميع فئات الاختبار، استخدِم التعليق التوضيحي @TestInstallIn من
القسم أعلاه. يمكنك بدلاً من ذلك وضع الربط التجريبي في وحدة test لاختبارات Robolectric، أو في وحدة androidTest لاختبار لقياس حالة التطبيق.
ننصحك باستخدام @TestInstallIn كلما أمكن ذلك.
ربط القيم الجديدة
استخدِم التعليق التوضيحي @BindValue لربط الحقول في الاختبار بسهولة بالرسم البياني لتبعية Hilt. يمكنك إضافة تعليق توضيحي إلى حقل باستخدام @BindValue وسيتم ربطه ضمن
نوع الحقل المُعلن عنه مع أي مؤهلات موجودة لهذا الحقل.
في مثال AnalyticsService، يمكنك استبدال AnalyticsService بـ
fake باستخدام @BindValue:
Kotlin
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsActivityTest { @BindValue @JvmField val analyticsService: AnalyticsService = FakeAnalyticsService() ... }
Java
@UninstallModules(AnalyticsModule.class) @HiltAndroidTest class SettingsActivityTest { @BindValue AnalyticsService analyticsService = FakeAnalyticsService(); ... }
يؤدي ذلك إلى تبسيط عملية استبدال الربط والإشارة إليه في الاختبار من خلال السماح لك بإجراء كليهما في الوقت نفسه.
يعمل @BindValue مع المؤهلات والتعليقات التوضيحية الأخرى للاختبار. على سبيل المثال، إذا كنت تستخدم مكتبات اختبار مثل Mockito، يمكنك استخدامها في اختبار Robolectric على النحو التالي:
Kotlin
... class SettingsActivityTest { ... @BindValue @ExampleQualifier @Mock lateinit var qualifiedVariable: ExampleCustomType // Robolectric tests here }
Java
... class SettingsActivityTest { ... @BindValue @ExampleQualifier @Mock ExampleCustomType qualifiedVariable; // Robolectric tests here }
إذا كنت بحاجة إلى إضافة ربط متعدد،
يمكنك استخدام التعليقَين التوضيحيَين @BindValueIntoSet و@BindValueIntoMap بدلاً من @BindValue. يتطلب @BindValueIntoMap منك أيضًا إضافة تعليق توضيحي للحقل
باستخدام تعليق توضيحي لمفتاح الخريطة.
حالات خاصة
توفّر Hilt أيضًا ميزات تتيح حالات الاستخدام غير العادية.
تطبيق مخصّص للاختبارات
إذا لم تتمكّن من استخدام HiltTestApplication لأنّ تطبيق الاختبار يحتاج إلى
توسيع تطبيق آخر، يمكنك إضافة تعليقات توضيحية إلى صنف أو واجهة جديدة باستخدام
@CustomTestApplication، مع تمرير قيمة الصنف الأساسي الذي تريد
توسيع تطبيق Hilt الذي تم إنشاؤه.
ستنشئ @CustomTestApplication فئة Application جاهزة للاختبار
باستخدام Hilt التي توسّع التطبيق الذي تم تمريره كمَعلمة.
Kotlin
@CustomTestApplication(BaseApplication::class) interface HiltTestApplication
Java
@CustomTestApplication(BaseApplication.class) interface HiltTestApplication { }
في المثال، تنشئ Hilt فئة Application باسم
HiltTestApplication_Application تمدّد فئة BaseApplication. بشكل عام، يكون اسم التطبيق الذي تم إنشاؤه هو اسم الفئة التي تم التعليق عليها والمُلحقة بـ _Application. يجب ضبط تطبيق اختبار Hilt الذي تم إنشاؤه ليتم تشغيله في اختبارات قياس حالة التطبيق أو اختبارات Robolectric كما هو موضّح في تطبيق الاختبار.
كائنات TestRule متعددة في اختبار لقياس حالة التطبيق
إذا كانت لديك عناصر TestRule أخرى في الاختبار، هناك طرق متعددة
لضمان عمل جميع القواعد معًا.
يمكنك تجميع القواعد معًا على النحو التالي:
Kotlin
@HiltAndroidTest class SettingsActivityTest { @get:Rule var rule = RuleChain.outerRule(HiltAndroidRule(this)). around(SettingsActivityTestRule(...)) // UI tests here. }
Java
@HiltAndroidTest public final class SettingsActivityTest { @Rule public RuleChain rule = RuleChain.outerRule(new HiltAndroidRule(this)) .around(new SettingsActivityTestRule(...)); // UI tests here. }
يمكنك بدلاً من ذلك استخدام كلتا القاعدتَين على المستوى نفسه طالما أنّ HiltAndroidRule يتم تنفيذها أولاً. حدِّد ترتيب التنفيذ باستخدام السمة order في التعليق التوضيحي @Rule. لا يعمل هذا إلا في إصدار JUnit
4.13 أو أعلى:
Kotlin
@HiltAndroidTest class SettingsActivityTest { @get:Rule(order = 0) var hiltRule = HiltAndroidRule(this) @get:Rule(order = 1) var settingsActivityTestRule = SettingsActivityTestRule(...) // UI tests here. }
Java
@HiltAndroidTest public final class SettingsActivityTest { @Rule(order = 0) public HiltAndroidRule hiltRule = new HiltAndroidRule(this); @Rule(order = 1) public SettingsActivityTestRule settingsActivityTestRule = new SettingsActivityTestRule(...); // UI tests here. }
launchFragmentInContainer
لا يمكن استخدام launchFragmentInContainer من مكتبة androidx.fragment:fragment-testing مع Hilt، لأنّه يعتمد على نشاط لم تتم إضافة تعليق توضيحي له باستخدام @AndroidEntryPoint.
استخدِم الرمز
launchFragmentInHiltContainer
من مستودع
architecture-samples على GitHub
بدلاً من ذلك.