Einer der Vorteile von Dependency Injection-Frameworks wie Hilt ist, dass sie das Testen von Code erleichtern.
Einheitentests
Für Unittests ist kein „Hilt“ erforderlich, da beim Testen einer Klasse, die Konstruktor einschleusen, müssen Sie diese Klasse nicht mit Hilt instanziieren. Stattdessen können Sie einen Klassenkonstruktor direkt aufrufen, indem Sie fake oder Mock übergeben. Abhängigkeiten zu verwenden, so als ob der Konstruktor nicht annotiert würde:
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 fügt Hilt Abhängigkeiten so ein, wie sie es in Ihrer Produktion tun würden. Code. Tests mit Hilt sind wartungsfrei, da Hilt automatisch generiert für jeden Test einen neuen Satz von Komponenten.
Testabhängigkeiten hinzufügen
Wenn Sie Hilt in Ihren Tests verwenden möchten, nehmen Sie die Abhängigkeit hilt-android-testing
in Ihr
Projekt:
Cool
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") }
UI-Test einrichten
Sie müssen alle UI-Tests, die Hilt verwenden, mit @HiltAndroidTest
annotieren. Dieses
-Annotation ist für das Generieren der Hilt-Komponenten für jeden Test verantwortlich.
Außerdem musst du der Testklasse das HiltAndroidRule
hinzufügen. Es verwaltet die
Komponenten und wird verwendet, um eine Einfügung in Ihren 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 Informationen zur Application
-Klasse haben, die Hilt
automatisch für Sie erstellt.
Anwendung testen
Sie müssen instrumentierte Tests ausführen, bei denen Hilt in einem Application
-Objekt verwendet wird
das Hilt unterstützt. Die Bibliothek stellt HiltTestApplication
zur Verwendung in Tests bereit.
Wenn für Ihre Tests eine andere Basisanwendung erforderlich ist, lesen Sie den Abschnitt Benutzerdefinierte Anwendung für
Tests.
Sie müssen die Testanwendung so einstellen, dass sie in Ihrem instrumentierten Code ausgeführt wird. Tests oder Robolectric Tests durchführen. Die folgenden Anweisungen sind nicht speziell für Hilt, aber sind allgemeine Richtlinien zur Angabe eines benutzerdefinierten die in Tests ausgeführt werden soll.
Testanwendung in instrumentierten Tests festlegen
So verwenden Sie die Hilt-Testanwendung in instrumentierten Tests müssen Sie einen neuen Test-Runner konfigurieren. Daher funktioniert Hilt für alle instrumentierten Tests in Ihrem Projekt. Ausführen führen Sie die folgenden Schritte aus:
- Benutzerdefinierte Klasse erstellen, die
AndroidJUnitRunner
Zoll OrdnerandroidTest
. - Überschreiben Sie die Funktion
newApplication
und übergeben Sie den Namen des 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 als Nächstes diesen Test-Runner in Ihrer Gradle-Datei, wie in der Instrumented Unit Test . Achten Sie darauf, verwenden Sie den vollständigen Klassenpfad:
Cool
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 Ihre UI-Ebene mit Robolectric testen, können Sie angeben, welche Anwendung
zur Verwendung in der Datei robolectric.properties
:
application = dagger.hilt.android.testing.HiltTestApplication
Alternativ können Sie die Anwendung bei jedem Test einzeln konfigurieren, indem Sie
mit der Annotation @Config
von 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. }
Wenn Sie eine ältere Version des Android-Gradle-Plug-ins als 4.2 verwenden, aktivieren Sie
Transformieren von @AndroidEntryPoint
-Klassen in lokalen Einheitentests durch Anwenden der
folgende Konfiguration in der build.gradle
-Datei Ihres Moduls:
Groovy
hilt { enableTransformForLocalTests = true }
Kotlin
hilt { enableTransformForLocalTests = true }
Weitere Informationen zu enableTransformForLocalTests
im Hilt
Dokumentation.
Testfunktionen
Sobald Hilt in Ihren Tests einsatzbereit ist, können Sie verschiedene Funktionen nutzen, Anpassung des Testprozesses.
Injektionstypen in Tests
Verwende @Inject
für die Feldeinschleusung, um Typen in einen Test einzufügen. Um Hilt zu bitten,
@Inject
-Felder mit Daten füllen, rufen Sie hiltRule.inject()
auf.
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 Fake- oder Simulationsinstanz einer Abhängigkeit einschleusen müssen, müssen Sie Verwenden Sie nicht die Bindung aus dem Produktionscode und eine andere. Um eine Bindung zu ersetzen, müssen Sie das Modul ersetzen, enthält die Bindung mit einem Testmodul, das die gewünschten Bindungen enthält. die im Test verwendet werden sollen.
Angenommen, Ihr Produktionscode deklariert eine Bindung für
AnalyticsService
so:
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 ); }
Erstellen Sie zum Ersetzen der AnalyticsService
-Bindung in Tests ein neues Hilt-Modul in
den Ordner test
oder androidTest
mit der fiktiven Abhängigkeit und annotieren
mit @TestInstallIn
. Alle Tests in diesem Ordner werden mit der Fake-Instanz eingeschleust
Abhängigkeit.
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 statt in allen Tests ersetzen möchten, deinstallieren Sie einen „Hilt“
Modul aus einem Test mit der Annotation @UninstallModules
und erstellen Sie ein neues
Testmoduls innerhalb des Tests.
Folgen Sie dem AnalyticsService
-Beispiel aus der vorherigen Version.
Hilt, um das Produktionsmodul mithilfe der Annotation @UninstallModules
zu ignorieren
in der Testklasse:
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 nur die Bindung für eine einzelne Testklasse ersetzt. Wenn Sie
Bindung für alle Testklassen verwenden, verwenden Sie die Annotation @TestInstallIn
aus der
weiter oben. Alternativ können Sie die Testbindung in das Modul test
einfügen.
für Robolectric-Tests und im androidTest
-Modul für instrumentierte Tests.
Es wird empfohlen, nach Möglichkeit @TestInstallIn
zu verwenden.
Neue Werte binden
Mit der Annotation @BindValue
kannst du Felder in deinem Test ganz einfach in „Hilt“ binden.
Abhängigkeitsdiagramm. Wenn Sie ein Feld mit @BindValue
annotieren, wird es gebunden unter
Den deklarierten Feldtyp mit allen für dieses Feld vorhandenen Qualifizierern.
Im Beispiel AnalyticsService
können Sie AnalyticsService
durch einen
Fälschung mit @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(); ... }
Dies vereinfacht sowohl das Ersetzen einer Bindung als auch den Verweis auf eine Bindung im Test. weil Sie beides gleichzeitig tun können.
@BindValue
funktioniert mit Qualifizierern und anderen Testannotationen. Beispiel:
wenn Sie Testbibliotheken wie
Mockito.
Robolectric-Test:
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 Multibinding hinzufügen müssen,
können Sie die Annotationen @BindValueIntoSet
und @BindValueIntoMap
verwenden.
von @BindValue
. @BindValueIntoMap
erfordert, dass Sie das Feld ebenfalls annotieren
mit einer Map Key-Anmerkung.
Besondere Fälle
Hilt bietet auch Funktionen für nicht standardmäßige Anwendungsfälle.
Benutzerdefinierte Anwendung für Tests
Wenn Sie HiltTestApplication
nicht verwenden können, weil Ihre Testanwendung
um eine andere Anwendung zu erweitern, eine neue Klasse oder Schnittstelle mit
@CustomTestApplication
, wobei der Wert der Basisklasse übergeben wird, für die der
die Hilt-Anwendung
erweitert werden soll.
@CustomTestApplication
generiert eine Application
-Klasse, die zum Testen bereit ist
mit Hilt, das die als Parameter übergebene Anwendung erweitert.
Kotlin
@CustomTestApplication(BaseApplication::class) interface HiltTestApplication
Java
@CustomTestApplication(BaseApplication.class) interface HiltTestApplication { }
In diesem Beispiel generiert Hilt eine Application
namens
HiltTestApplication_Application
, die die Klasse BaseApplication
erweitert. In
allgemein ist der Name der generierten Anwendung der Name der annotierten
Klasse mit angehängtem _Application
. Sie müssen den generierten Hilt-Test
die Sie in Ihren instrumentierten Tests ausführen können, oder
Robolectric-Tests, wie unter Test
.
Mehrere TestRule-Objekte in Ihrem instrumentierten Test
Wenn Sie andere TestRule
-Objekte in Ihrem Test haben, gibt es mehrere Möglichkeiten,
dass alle Regeln ineinandergreifen.
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,
HiltAndroidRule
wird zuerst ausgeführt. Geben Sie die Ausführungsreihenfolge mithilfe der
order
-Attribut in der Anmerkung @Rule
. Das funktioniert nur mit 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
launchFragmentInContainer
kann nicht aus dem
androidx.fragment:fragment-testing
Bibliothek in Hilt, weil sie auf einem
Aktivität, die nicht mit @AndroidEntryPoint
gekennzeichnet ist.
Verwenden Sie die Methode
launchFragmentInHiltContainer
Code aus der
architecture-samples
GitHub
-Repository.
Verwenden Sie einen Einstiegspunkt, bevor die Singleton-Komponente verfügbar ist
Die Anmerkung @EarlyEntryPoint
bietet eine Ausstiegsmöglichkeit, wenn ein „Hilt“-Eintrag
muss erstellt werden, bevor die Singleton-Komponente
Hilt-Test.
Weitere Informationen zu @EarlyEntryPoint
finden Sie in der
Hilt-Dokumentation