אחד מהיתרונות של שימוש ב-frameworks של החדרת תלות כמו Hilt הוא שהם מקלים על בדיקת הקוד.
בדיקות יחידה (unit testing)
לא צריך את Hilt לבדיקות יחידה, כי כשבודקים מחלקה שמשתמשת בהחדרה ל-constructor, לא צריך להשתמש ב-Hilt כדי ליצור מופע של המחלקה הזו. במקום זאת, אתם יכולים לקרוא ישירות ל-constructor של הכיתה על ידי העברת יחסי תלות מזויפים או מדומים, בדיוק כמו שהייתם עושים אם ה-constructor לא היה מסומן:
@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(...) } }
אותו עיקרון חל על מחלקות ViewModel שמתקבלות על ידי קריאה ל-hiltViewModel() ב-composables. בבדיקות יחידה (unit testing), יוצרים את ה-ViewModel ישירות באמצעות אובייקטים מזויפים.
במאמרים מצב ו-Jetpack Compose ואיפה להעביר את המצב לרמה גבוהה יותר מוסבר איך המצב עובר מ-ViewModel לרכיבים קומפוזביליים.
בדיקות מקצה לקצה
בבדיקות שילוב, Hilt מחדיר תלויות כמו בקוד הייצור. בדיקה באמצעות Hilt לא דורשת תחזוקה כי Hilt יוצר באופן אוטומטי קבוצה חדשה של רכיבים לכל בדיקה.
הוספת יחסי תלות לבדיקה
כדי להשתמש ב-Hilt בבדיקות, צריך לכלול את התלות hilt-android-testing בפרויקט:
dependencies { // For Robolectric tests. testImplementation("com.google.dagger:hilt-android-testing:2.57.1") kspTest("com.google.dagger:hilt-android-compiler:2.57.1") // For instrumented tests. androidTestImplementation("com.google.dagger:hilt-android-testing:2.57.1") kspAndroidTest("com.google.dagger:hilt-android-compiler:2.57.1") // Compose UI test rule. androidTestImplementation("androidx.compose.ui:ui-test-junit4") debugImplementation("androidx.compose.ui:ui-test-manifest") }
הגדרת בדיקת ממשק משתמש
צריך להוסיף הערה לכל בדיקת ממשק משתמש שמשתמשת ב-Hilt עם @HiltAndroidTest. ההערה הזו אחראית ליצירת רכיבי Hilt לכל בדיקה.
בנוסף, צריך להוסיף את HiltAndroidRule למחלקת הבדיקה. הוא מנהל את המצב של הרכיבים ומשמש להזרקה בבדיקה:
@HiltAndroidTest class SettingsScreenTest { @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this) @get:Rule(order = 1) val composeRule = createAndroidComposeRule<HiltTestActivity>() // Compose UI tests here. }
בשלב הבא, הבדיקה צריכה לדעת על המחלקה Application ש-Hilt יוצר באופן אוטומטי.
כדי לאפשר ל-Hilt להחדיר יחסי תלות, צריך ליצור פעילות ריקה בשם HiltTestActivity בקבוצת המקורות androidTest ולהוסיף לה הערה עם @AndroidEntryPoint. לאחר מכן, createAndroidComposeRule משתמש בפעילות הזו כמארח לתוכן הניתן להרכבה.
אפליקציית בדיקה
צריך להריץ בדיקות אינסטרומנטציה שמשתמשות ב-Hilt באובייקט Application שתומך ב-Hilt. הספרייה מספקת את HiltTestApplication לשימוש בבדיקות.
אם לבדיקות שלכם נדרשת אפליקציית בסיס אחרת, עיינו בקטע אפליקציה בהתאמה אישית לבדיקות.
אתם צריכים להגדיר את אפליקציית הבדיקה כך שתפעל בבדיקות עם כלים או בבדיקות Robolectric. ההוראות הבאות לא ספציפיות ל-Hilt, אלא הנחיות כלליות איך לציין אפליקציה בהתאמה אישית להפעלה בבדיקות.
הגדרת אפליקציית הבדיקה בבדיקות אינסטרומנטציה
כדי להשתמש באפליקציית הבדיקה של Hilt בבדיקות עם כלים, צריך להגדיר כלי חדש להרצת בדיקות. כך Hilt יפעל בכל בדיקות האינסטרומנטציה בפרויקט. כך עושים את זה:
- יוצרים מחלקה בהתאמה אישית שמרחיבה את
AndroidJUnitRunnerבתיקייהandroidTest. - מבטלים את הפונקציה
newApplicationומעבירים את השם של אפליקציית הבדיקה שנוצרה ב-Hilt.
// 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) } }
לאחר מכן, מגדירים את כלי הבדיקה הזה בקובץ Gradle, כמו שמתואר במדריך לבדיקת יחידה עם אינסטרומנטציה. חשוב לוודא שאתם משתמשים בנתיב המלא של המחלקה:
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:
@HiltAndroidTest @Config(application = HiltTestApplication::class) class SettingsScreenTest { @get:Rule var hiltRule = HiltAndroidRule(this) // Robolectric tests here. }
תכונות בבדיקה
אחרי ש-Hilt מוכן לשימוש בבדיקות, אתם יכולים להשתמש בכמה תכונות כדי להתאים אישית את תהליך הבדיקה.
הזרקת סוגים בבדיקות
כדי להחדיר סוגים לבדיקה, משתמשים ב-@Inject להחדרה לשדה. כדי להורות ל-Hilt לאכלס את השדות של @Inject, צריך להפעיל את hiltRule.inject().
דוגמה לבדיקת אינסטרומנטציה:
@HiltAndroidTest class SettingsScreenTest { @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this) @get:Rule(order = 1) val composeRule = createAndroidComposeRule<HiltTestActivity>() @Inject lateinit var analyticsAdapter: AnalyticsAdapter @Before fun init() { hiltRule.inject() } @Test fun settingsScreen_showsTitle() { composeRule.setContent { SettingsScreen() } composeRule.onNodeWithText("Settings").assertIsDisplayed() // analyticsRepository is available here. } }
החלפת קישור
אם אתם צריכים להחדיר מופע מדומה או מזויף של תלות, אתם צריכים להורות ל-Hilt לא להשתמש בקישור שבו השתמשתם בקוד בסביבת הייצור, ולהשתמש בקישור אחר במקום זאת. כדי להחליף קישור, צריך להחליף את המודול שמכיל את הקישור במודול בדיקה שמכיל את הקישורים שבהם רוצים להשתמש בבדיקה.
לדוגמה, נניח שבקוד הייצור שלכם מוגדר קישור ל-AnalyticsService באופן הבא:
@Module @InstallIn(SingletonComponent::class) abstract class AnalyticsModule { @Singleton @Binds abstract fun bindAnalyticsService( analyticsServiceImpl: AnalyticsServiceImpl ): AnalyticsService }
כדי להחליף את הקישור של AnalyticsService בבדיקות, יוצרים מודול Hilt חדש בתיקייה test או androidTest עם התלות המזויפת ומוסיפים לה הערה עם @TestInstallIn. במקום זאת, כל הבדיקות בתיקייה הזו מוזרקות עם התלות המזויפת.
@Module @TestInstallIn( components = [SingletonComponent::class], replaces = [AnalyticsModule::class] ) abstract class FakeAnalyticsModule { @Singleton @Binds abstract fun bindAnalyticsService( fakeAnalyticsService: FakeAnalyticsService ): AnalyticsService }
מכיוון שרכיבים קומפוזביליים בדרך כלל צורכים את יחסי התלות האלה באופן עקיף דרך ViewModel שמתקבל באמצעות hiltViewModel(), מספיק להחליף את הקישור ב-Hilt. הקומפוזבילי שנבדק אוסף את הזיוף באופן אוטומטי.
החלפת קישור בבדיקה אחת
כדי להחליף קישור בבדיקה אחת במקום בכל הבדיקות, מסירים מודול Hilt מבדיקה באמצעות ההערה @UninstallModules ויוצרים מודול בדיקה חדש בתוך הבדיקה.
בהמשך לדוגמה של AnalyticsService מהגרסה הקודמת, קודם כל צריך להורות ל-Hilt להתעלם מהמודול של סביבת הייצור באמצעות ההערה @UninstallModules במחלקת הבדיקה:
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsScreenTest { ... }
בשלב הבא, צריך להחליף את הקישור. יוצרים מודול חדש בתוך מחלקת הבדיקה שמגדיר את הקישור לבדיקה:
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsScreenTest { @Module @InstallIn(SingletonComponent::class) abstract class TestModule { @Singleton @Binds abstract fun bindAnalyticsService( fakeAnalyticsService: FakeAnalyticsService ): AnalyticsService } // ... }
הפעולה הזו מחליפה את הקישור רק למחלקת בדיקה אחת. אם רוצים להחליף את הקישור לכל מחלקות הבדיקה, משתמשים בהערה @TestInstallIn מהקטע שלמעלה. לחלופין, אפשר להוסיף את הקישור לבדיקה למודול test לבדיקות Robolectric, או למודול androidTest לבדיקות אינסטרומנטציה.
מומלץ להשתמש ב-@TestInstallIn כשאפשר.
קישור ערכים חדשים
משתמשים בהערה @BindValue כדי לקשר בקלות שדות בבדיקה לתרשים התלות של Hilt. הוספת הערה לשדה באמצעות @BindValue, והוא יקושר לסוג השדה שהוצהר עם כל המאפיינים שקיימים בשדה הזה.
בדוגמה של AnalyticsService, אפשר להחליף את AnalyticsService ב-fake באמצעות @BindValue:
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsScreenTest { @BindValue @JvmField val analyticsService: AnalyticsService = FakeAnalyticsService() ... }
כך קל יותר להחליף קישור ולהפנות לקישור בבדיקה, כי אפשר לעשות את שתי הפעולות בו-זמנית.
@BindValue פועל עם מגדירים והערות בדיקה אחרות. לדוגמה, אם אתם משתמשים בספריות בדיקה כמו Mockito, אתם יכולים להשתמש בהן בבדיקת Robolectric באופן הבא:
... class SettingsScreenTest { ... @BindValue @ExampleQualifier @Mock lateinit var qualifiedVariable: ExampleCustomType // Robolectric tests here }
אם אתם צריכים להוסיף multibinding, אתם יכולים להשתמש באנוטציות @BindValueIntoSet ו-@BindValueIntoMap במקום @BindValue. כדי להשתמש ב-@BindValueIntoMap, צריך להוסיף לשדה גם הערה עם מפתח מפה.
מקרים מיוחדים
Hilt מספקת גם תכונות שתומכות בתרחישי שימוש לא סטנדרטיים.
אפליקציה מותאמת אישית לבדיקות
אם אתם לא יכולים להשתמש ב-HiltTestApplication כי אפליקציית הבדיקה צריכה להרחיב אפליקציה אחרת, הוסיפו הערה לממשק או למחלקה חדשה באמצעות @CustomTestApplication, והעבירו את הערך של המחלקה הבסיסית שאתם רוצים שאפליקציית Hilt שנוצרה תרחיב.
@CustomTestApplication ייצור מחלקת Application שמוכנה לבדיקה עם Hilt, שמרחיבה את האפליקציה שהעברתם כפרמטר.
@CustomTestApplication(BaseApplication::class) interface HiltTestApplication
בדוגמה, Hilt יוצר Application בשם HiltTestApplication_Application שמרחיב את המחלקה BaseApplication. באופן כללי, השם של האפליקציה שנוצרה הוא השם של המחלקה עם ההערות, בתוספת _Application. אתם צריכים להגדיר את אפליקציית הבדיקה של Hilt שנוצרה כך שתפעל בבדיקות אינסטרומנטציה או בבדיקות Robolectric, כפי שמתואר בקטע אפליקציית בדיקה.
מספר אובייקטים של TestRule בבדיקת אינסטרומנטציה
בדיקות של ממשק משתמש ב-Compose כבר משלבות את HiltAndroidRule עם כלל בדיקה של Compose, כמו createAndroidComposeRule. אם יש לכם אובייקטים נוספים של TestRule, ודאו ש-HiltAndroidRule פועל קודם. מצהירים על סדר ההרצה באמצעות המאפיין order ב-@Rule:
@HiltAndroidTest class SettingsScreenTest { @get:Rule(order = 0) var hiltRule = HiltAndroidRule(this) @get:Rule(order = 1) val composeRule = createAndroidComposeRule<HiltTestActivity>() @get:Rule(order = 2) val otherRule = SomeOtherRule() // UI tests here. }
אפשר גם לעטוף את הכללים ב-RuleChain, ולהגדיר את HiltAndroidRule ככלל החיצוני.
@HiltAndroidTest class SettingsScreenTest { @get:Rule var rule = RuleChain.outerRule(HiltAndroidRule(this)). around(SettingsScreenTestRule(...)) // UI tests here. }
שימוש בנקודת כניסה לפני שרכיב ה-singleton זמין
ההערה @EarlyEntryPoint מספקת פתח מילוט כשצריך ליצור נקודת כניסה של Hilt לפני שרכיב ה-singleton זמין בבדיקת Hilt.
מידע נוסף על @EarlyEntryPoint זמין במסמכי התיעוד של Hilt.
מקורות מידע נוספים
איפה אפשר למצוא מידע נוסף על בדיקות?