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

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

בדיקות יחידה

אין צורך ב-Hilt לבדיקות יחידה, כי כשבודקים כיתה שמשתמשת בהזרקה ל-constructor, אין צורך להשתמש ב-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 בפרויקט:

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

Groovy

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 להחדרת שדות. כדי להורות ל-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 פועל עם מכשירים למיון (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 שנוצרה כך שתופעל בבדיקות עם מכשירי מעקב או בבדיקות 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 מהמאגר ב-GitHub‏ architecture-samples.

שימוש בנקודת כניסה לפני שרכיב ה-Singleton זמין

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

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