دليل اختبار Hilt

من بين مزايا استخدام إطارات عمل حقن التبعية، مثل 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.51.1'
    // ...with Kotlin.
    kaptTest 'com.google.dagger:hilt-android-compiler:2.51.1'
    // ...with Java.
    testAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.51.1'


    // For instrumented tests.
    androidTestImplementation 'com.google.dagger:hilt-android-testing:2.51.1'
    // ...with Kotlin.
    kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.51.1'
    // ...with Java.
    androidTestAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.51.1'
}

Kotlin

dependencies {
    // For Robolectric tests.
    testImplementation("com.google.dagger:hilt-android-testing:2.51.1")
    // ...with Kotlin.
    kaptTest("com.google.dagger:hilt-android-compiler:2.51.1")
    // ...with Java.
    testAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.51.1")


    // For instrumented tests.
    androidTestImplementation("com.google.dagger:hilt-android-testing:2.51.1")
    // ...with Kotlin.
    kaptAndroidTest("com.google.dagger:hilt-android-compiler:2.51.1")
    // ...with Java.
    androidTestAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.51.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 لجميع الاختبارات المعملية التي يتم إجراؤها في مشروعك. اتّبِع الخطوات التالية:

  1. إنشاء فئة مخصصة تمتد AndroidJUnitRunner بوصة المجلد androidTest.
  2. تجاهُل الدالة 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.
}

إذا كنت تستخدم إصدار مكوّن Android Gradle الإضافي أقل من 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.
  }
}

استبدال عملية ربط

إذا كنت بحاجة إلى إدخال مثيل وهمي أو وهمي للتبعية، فأنت بحاجة إلى إخبار يجب عدم استخدام الربط الذي تم استخدامه في رمز الإنتاج واستخدام مختلفة بدلاً من ذلك. لاستبدال أي ربط، يجب استبدال الوحدة التي يحتوي على الربط بوحدة اختبار تحتوي على الروابط التي تريد لاستخدامها في الاختبار.

على سبيل المثال، لنفترض أنّ رمز الإنتاج يعلن عن ربط لملف برمجي 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 من النسخة السابقة، ابدأ بإخبار انقر لتجاهل وحدة الإنتاج باستخدام التعليق التوضيحي @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. بشكلٍ عام، اسم التطبيق الذي تم إنشاؤه هو اسم ال annotated class المُرفَق بـ _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.
}

إطلاقFragmentInContainer

لا يمكن استخدام launchFragmentInContainer من مكتبة androidx.fragment:fragment-testing مع Hilt، لأنّه يعتمد على نشاط لم يتمّت إضافة تعليقات توضيحية إليه باستخدام @AndroidEntryPoint.

استخدِم رمز launchFragmentInHiltContainer من مستودع GitHub architecture-samples بدلاً من ذلك.

استخدام نقطة دخول قبل أن يصبح المكوِّن المفردتون متاحًا

يقدّم التعليق التوضيحي @EarlyEntryPoint مخرجًا عند الحاجة إلى إنشاء نقطة دخول Hilt قبل توفّر المكوّن الفردي في اختبار Hilt.

يمكنك الاطّلاع على مزيد من المعلومات عن @EarlyEntryPoint في مستندات Hilt.