מדריך לבדיקת נסתר

אחד מהיתרונות של שימוש ב-frameworks להזרקת יחסי תלות כמו Hilt הוא שהם מאפשרים לבדוק את הקוד בקלות רבה יותר.

בדיקות יחידה

Hilt לא נחוץ לבדיקות יחידה, כי כשבודקים מחלקה שמשתמשת החדרת constructor, לא צריך להשתמש ב-Hilt כדי ליצור את המחלקה הזו. במקום זאת, תוכלו לקרוא ישירות ל-constructor של הכיתה על ידי העברת זיוף או חיקוי של יחסי התלות, בדיוק כמו שהיית עושה אם לא היו מוסיפים הערות ל-constructor:

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 ב project:

Groovy

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, כפי שמתואר בדיקת יחידה אינסטרומנטלית guide. כדאי לוודא משתמשים בנתיב הכיתה המלא:

מגניב

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 Plugin בגרסה נמוכה מ-4.2, תוכלו להפעיל את הטרנספורמציה של כיתות @AndroidEntryPoint בבדיקות יחידה מקומיות על ידי החלת ההגדרה הבאה בקובץ build.gradle של המודול:

Groovy

hilt {
    enableTransformForLocalTests = true
}

Kotlin

hilt {
    enableTransformForLocalTests = true
}

מידע נוסף על enableTransformForLocalTests זמין במסמכי התיעוד של Hilt.

בדיקת תכונות

אחרי ש-Hilt מוכן לשימוש בבדיקות, אפשר להשתמש בכמה תכונות כדי להתאים אישית את תהליך הבדיקה.

סוגי החדרה בבדיקות

כדי להזריק סוגים לבדיקה, צריך להשתמש ב-@Inject להחדרת שדה. כדי לומר ל-Hhilt לאכלס את השדות @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 לבדיקות רובולקטריות, או במודול 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 פועל עם מכשירים למיון (qualifiers) והערות בדיקה אחרות. לדוגמה, אם אתם משתמשים בספריות בדיקה כמו 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 שנוצרה שתרוץ בבדיקות אינסטרומנטליות או בדיקות רובולקטריות כפי שמתואר בבדיקה .

מספר אובייקטי 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 במקום זאת.

צריך להשתמש בנקודת כניסה לפני שרכיב הסינגלטון זמין

ההערה @EarlyEntryPoint מספקת פתח מילוט כשרשומת Hilt צריך ליצור נקודה לפני שרכיב הסינגלטון יהיה זמין בדיקת השהיה.

מידע נוסף על @EarlyEntryPoint זמין במסמכי התיעוד של Hilt.