من بين مزايا استخدام أُطر عمل حقن التبعية، مثل 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 لاستخدامها في الاختبارات.
إذا كانت اختباراتك بحاجة إلى تطبيق أساسي مختلف، يمكنك الاطّلاع على مقالة تطبيق مخصّص لـ
الاختبارات.
عليك ضبط تطبيق الاختبار لتشغيله في اختباراتك التي تم تتبُّعها instrumented tests أو اختبارات Robolectric tests. لا تنطبق التعليمات التالية على 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
بدلاً من ذلك، يمكنك ضبط التطبيق في كل اختبار على حدة باستخدام تصنيف @Config في Robolectric:
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 ببديل وهمي باستخدام @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. لا يعمل ذلك إلا في الإصدار 4.13 من JUnit أو الإصدارات الأحدث:
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
بدلاً من ذلك.