Einer der Vorteile der Verwendung von Frameworks für die Abhängigkeitsinjektion wie Hilt ist, dass das Testen von Code dadurch einfacher wird.
Einheitentests
Hilt ist für Einheitentests nicht erforderlich. Wenn Sie eine Klasse testen, die die Konstruktorinjektion verwendet, müssen Sie Hilt nicht verwenden, um diese Klasse zu instanziieren. Stattdessen können Sie direkt einen Klassenkonstruktor aufrufen, indem Sie gefälschte oder simulierte Abhängigkeiten übergeben, so als wäre der Konstruktor nicht mit einer Annotation versehen:
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(...); } }
End-to-End-Tests
Bei Integrationstests injiziert Hilt Abhängigkeiten so, wie es im Produktionscode der Fall wäre. Tests mit Hilt erfordern keine Wartung, da Hilt für jeden Test automatisch einen neuen Satz von Komponenten generiert.
Testabhängigkeiten hinzufügen
Wenn Sie Hilt in Ihren Tests verwenden möchten, fügen Sie die Abhängigkeit hilt-android-testing in Ihr Projekt ein:
Groovy
dependencies { // For Robolectric tests. testImplementation 'com.google.dagger:hilt-android-testing:2.57.1' // ...with Kotlin. kaptTest 'com.google.dagger:hilt-android-compiler:2.57.1' // ...with Java. testAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.57.1' // For instrumented tests. androidTestImplementation 'com.google.dagger:hilt-android-testing:2.57.1' // ...with Kotlin. kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.57.1' // ...with Java. androidTestAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.57.1' }
Kotlin
dependencies { // For Robolectric tests. testImplementation("com.google.dagger:hilt-android-testing:2.57.1") // ...with Kotlin. kaptTest("com.google.dagger:hilt-android-compiler:2.57.1") // ...with Java. testAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.57.1") // For instrumented tests. androidTestImplementation("com.google.dagger:hilt-android-testing:2.57.1") // ...with Kotlin. kaptAndroidTest("com.google.dagger:hilt-android-compiler:2.57.1") // ...with Java. androidTestAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.57.1") }
UI-Test einrichten
Sie müssen alle UI-Tests, die Hilt verwenden, mit @HiltAndroidTest annotieren. Diese Annotation ist für die Generierung der Hilt-Komponenten für jeden Test verantwortlich.
Außerdem müssen Sie der Testklasse HiltAndroidRule hinzufügen. Sie verwaltet den Status der Komponenten und wird verwendet, um die Injektion in Ihrem Test durchzuführen:
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. }
Als Nächstes muss Ihr Test die Application-Klasse kennen, die Hilt automatisch für Sie generiert.
Testanwendung
Sie müssen instrumentierte Tests, die Hilt verwenden, in einem Application-Objekt ausführen, das Hilt unterstützt. Die Bibliothek stellt HiltTestApplication für die Verwendung in Tests bereit.
Wenn für Ihre Tests eine andere Basisanwendung erforderlich ist, lesen Sie Benutzerdefinierte Anwendung für
Tests.
Sie müssen Ihre Testanwendung so einstellen, dass sie in Ihren instrumentierten Tests oder Robolectric Tests ausgeführt wird. Die folgende Anleitung bezieht sich nicht speziell auf Hilt, sondern enthält allgemeine Richtlinien zum Angeben einer benutzerdefinierten Anwendung, die in Tests ausgeführt werden soll.
Testanwendung in instrumentierten Tests festlegen
Wenn Sie die Hilt-Testanwendung in instrumentierten Tests verwenden möchten, müssen Sie einen neuen Test-Runner konfigurieren. Dadurch funktioniert Hilt für alle instrumentierten Tests in Ihrem Projekt. Führen Sie die folgenden Schritte aus:
- Erstellen Sie im Ordner
androidTesteine benutzerdefinierte Klasse, dieAndroidJUnitRunnerin erweitert. - Überschreiben Sie die Funktion
newApplicationund übergeben Sie den Namen der generierten Hilt-Testanwendung.
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); } }
Konfigurieren Sie diesen Test-Runner als Nächstes in Ihrer Gradle-Datei, wie im Leitfaden zu instrumentierten Einheitentests beschrieben. Verwenden Sie den vollständigen Klassenpfad:
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" } }
Testanwendung in Robolectric-Tests festlegen
Wenn Sie Robolectric zum Testen Ihrer UI-Ebene verwenden, können Sie in der Datei robolectric.properties angeben, welche Anwendung verwendet werden soll:
application = dagger.hilt.android.testing.HiltTestApplication
Alternativ können Sie die Anwendung für jeden Test einzeln konfigurieren, indem Sie die @Config-Annotation von Robolectric verwenden:
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. }
Wenn Sie eine Android-Gradle-Plug-in-Version unter 4.2 verwenden, aktivieren Sie die Transformation von @AndroidEntryPoint-Klassen in lokalen Einheitentests, indem Sie die folgende Konfiguration in der Datei build.gradle Ihres Moduls anwenden:
Groovy
hilt { enableTransformForLocalTests = true }
Kotlin
hilt { enableTransformForLocalTests = true }
Weitere Informationen zu enableTransformForLocalTests finden Sie in der Hilt
Dokumentation.
Testfunktionen
Sobald Hilt in Ihren Tests verwendet werden kann, können Sie verschiedene Funktionen verwenden, um den Testprozess anzupassen.
Typen in Tests einschleusen
Verwenden Sie @Inject für die Field Injection, um Typen in einen Test einzuschleusen. Rufen Sie hiltRule.inject() auf, um Hilt anzuweisen, die @Inject-Felder auszufüllen.
Hier ein Beispiel für einen instrumentierten Test:
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. } }
Bindung ersetzen
Wenn Sie eine gefälschte oder simulierte Instanz einer Abhängigkeit einschleusen müssen, müssen Sie Hilt anweisen, die im Produktionscode verwendete Bindung nicht zu verwenden und stattdessen eine andere zu verwenden. Um eine Bindung zu ersetzen, müssen Sie das Modul, das die Bindung enthält, durch ein Testmodul ersetzen, das die Bindungen enthält, die Sie im Test verwenden möchten.
Angenommen, in Ihrem Produktionscode wird eine Bindung für AnalyticsService wie folgt deklariert:
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 ); }
Wenn Sie die AnalyticsService-Bindung in Tests ersetzen möchten, erstellen Sie im Ordner test oder androidTest ein neues Hilt-Modul mit der gefälschten Abhängigkeit und annotieren Sie es mit @TestInstallIn. Alle Tests in diesem Ordner werden stattdessen mit der gefälschten Abhängigkeit injiziert.
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 ); }
Bindung in einem einzelnen Test ersetzen
Wenn Sie eine Bindung in einem einzelnen Test anstelle aller Tests ersetzen möchten, deinstallieren Sie ein Hilt-Modul aus einem Test mit der Annotation @UninstallModules und erstellen Sie im Test ein neues Testmodul.
Folgen Sie dem Beispiel AnalyticsService aus der vorherigen Version und weisen Sie Hilt zuerst an, das Produktionsmodul zu ignorieren, indem Sie die Annotation @UninstallModules in der Testklasse verwenden:
Kotlin
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsActivityTest { ... }
Java
@UninstallModules(AnalyticsModule.class) @HiltAndroidTest public final class SettingsActivityTest { ... }
Als Nächstes müssen Sie die Bindung ersetzen. Erstellen Sie in der Testklasse ein neues Modul, das die Testbindung definiert:
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 ); } ... }
Dadurch wird die Bindung nur für eine einzelne Testklasse ersetzt. Wenn Sie die Bindung für alle Testklassen ersetzen möchten, verwenden Sie die Annotation @TestInstallIn aus dem obigen Abschnitt. Alternativ können Sie die Testbindung für Robolectric-Tests in das Modul test oder für instrumentierte Tests in das Modul androidTest einfügen.
Es wird empfohlen, @TestInstallIn nach Möglichkeit zu verwenden.
Neue Werte binden
Mit der Annotation @BindValue können Sie Felder in Ihrem Test ganz einfach in den Hilt-Abhängigkeitsgraphen einbinden. Wenn Sie ein Feld mit @BindValue annotieren, wird es unter dem deklarierten Feldtyp mit allen für dieses Feld vorhandenen Qualifizierern gebunden.
Im Beispiel AnalyticsService können Sie AnalyticsService mit @BindValue durch eine gefälschte Instanz ersetzen:
Kotlin
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsActivityTest { @BindValue @JvmField val analyticsService: AnalyticsService = FakeAnalyticsService() ... }
Java
@UninstallModules(AnalyticsModule.class) @HiltAndroidTest class SettingsActivityTest { @BindValue AnalyticsService analyticsService = FakeAnalyticsService(); ... }
Dadurch wird sowohl das Ersetzen einer Bindung als auch das Verweisen auf eine Bindung in Ihrem Test vereinfacht, da Sie beides gleichzeitig tun können.
@BindValue funktioniert mit Qualifizierern und anderen Testannotationen. Wenn Sie beispielsweise
Testbibliotheken wie
Mockitoverwenden, können Sie sie in einem
Robolectric-Test wie folgt verwenden:
Kotlin
... class SettingsActivityTest { ... @BindValue @ExampleQualifier @Mock lateinit var qualifiedVariable: ExampleCustomType // Robolectric tests here }
Java
... class SettingsActivityTest { ... @BindValue @ExampleQualifier @Mock ExampleCustomType qualifiedVariable; // Robolectric tests here }
Wenn Sie eine Multibindung hinzufügen müssen,
können Sie anstelle
von @BindValue die Annotationen @BindValueIntoSet und @BindValueIntoMap verwenden. Bei @BindValueIntoMap müssen Sie das Feld auch mit einer Annotation für den Mapschlüssel annotieren.
Sonderfälle
Hilt bietet auch Funktionen zur Unterstützung von nicht standardmäßigen Anwendungsfällen.
Benutzerdefinierte Anwendung für Tests
Wenn Sie HiltTestApplication nicht verwenden können, weil Ihre Testanwendung eine andere Anwendung erweitern muss, annotieren Sie eine neue Klasse oder Schnittstelle mit @CustomTestApplication und übergeben Sie den Wert der Basisklasse, die die generierte Hilt-Anwendung erweitern soll.
@CustomTestApplication generiert eine Application Klasse, die für Tests bereit ist
mit Hilt, die die Anwendung erweitert, die Sie als Parameter übergeben haben.
Kotlin
@CustomTestApplication(BaseApplication::class) interface HiltTestApplication
Java
@CustomTestApplication(BaseApplication.class) interface HiltTestApplication { }
Im Beispiel generiert Hilt eine Application mit dem Namen
HiltTestApplication_Application, die die BaseApplication Klasse erweitert. Im Allgemeinen ist der Name der generierten Anwendung der Name der annotierten Klasse mit dem Suffix _Application. Sie müssen die generierte Hilt-Test
anwendung so einstellen, dass sie in Ihren instrumentierten Tests oder
Robolectric-Tests ausgeführt wird, wie unter Test
anwendung beschrieben.
Mehrere TestRule-Objekte in Ihrem instrumentierten Test
Wenn Sie andere TestRule-Objekte in Ihrem Test haben, gibt es mehrere Möglichkeiten, um sicherzustellen, dass alle Regeln zusammen funktionieren.
Sie können die Regeln wie folgt zusammenfassen:
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. }
Alternativ können Sie beide Regeln auf derselben Ebene verwenden, solange HiltAndroidRule zuerst ausgeführt wird. Geben Sie die Ausführungsreihenfolge mit dem Attribut order in der Annotation @Rule an. Dies funktioniert nur in JUnit-Version 4.13 oder höher:
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
Es ist nicht möglich, launchFragmentInContainer aus der Bibliothek androidx.fragment:fragment-testing mit Hilt zu verwenden, da es auf einer Aktivität basiert, die nicht mit @AndroidEntryPoint annotiert ist.
Verwenden Sie stattdessen den
launchFragmentInHiltContainer
Code aus dem
architecture-samples GitHub
Repository.
Einen Einstiegspunkt verwenden, bevor die Singleton-Komponente verfügbar ist
Die Annotation @EarlyEntryPoint bietet eine Möglichkeit, wenn ein Hilt-Einstiegspunkt erstellt werden muss, bevor die Singleton-Komponente in einem Hilt-Test verfügbar ist.
Weitere Informationen zu @EarlyEntryPoint finden Sie in der
Hilt-Dokumentation.