دليل اختبار 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

بدلاً من ذلك، يمكنك ضبط التطبيق في كل اختبار على حدة من خلال باستخدام تعليق Robolectric's @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.
}

إذا كنت تستخدم إصدار مكوّن Android Gradle الإضافي أقل من 4.2، فعِّله تحويل @AndroidEntryPoint فئة في اختبارات الوحدات المحلية من خلال تطبيق في ما يلي الإعدادات في ملف build.gradle بالوحدة:

رائع

hilt {
    enableTransformForLocalTests = true
}

Kotlin

hilt {
    enableTransformForLocalTests = true
}

مزيد من المعلومات عن "enableTransformForLocalTests" في الفلتر (Hilt) المستندات.

ميزات الاختبار

بعد أن تصبح Hilt جاهزة للاستخدام في اختباراتك، يمكنك استخدام عدة ميزات لإجراء ما يلي: تخصيص عملية الاختبار.

أنواع الحقن في الاختبارات

لحقن الأنواع في اختبار، استخدِم @Inject لحقن الحقل. لتطلب من "هيلت" املأ حقول @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 بـ زائفًا باستخدام @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.
}

إطلاقFragmentInContainer

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

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

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

يوفّر التعليق التوضيحي @EarlyEntryPoint بوابة هروب عند استخدام Hilt. قبل أن يصبح المكون المفردتون متاحًا في اختبار Hilt

مزيد من المعلومات حول "@EarlyEntryPoint" في مستندات النتيجة: