L'un des avantages des frameworks d'injection de dépendances comme Hilt est qu'ils facilitent le test de votre code.
Tests unitaires
Hilt n'est pas nécessaire pour les tests unitaires. En effet, lorsque vous testez une classe qui utilise l'injection de constructeur, vous n'avez pas besoin de l'utiliser pour instancier cette classe. À la place, vous pouvez appeler directement un constructeur de classe en transmettant des dépendances factices ou fictives, comme si le constructeur n'était pas annoté :
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(...); } }
Tests de bout en bout
Pour les tests d'intégration, Hilt injecte des dépendances comme il le ferait dans votre code de production. Effectuer des tests avec Hilt ne nécessite aucune maintenance, dans la mesure où un nouvel ensemble de composants est généré automatiquement pour chaque test.
Ajouter des dépendances de test
Pour utiliser Hilt dans vos tests, incluez la dépendance hilt-android-testing
dans votre projet :
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") }
Configuration du test d'interface utilisateur
Vous devez annoter avec @HiltAndroidTest
tout test d'interface utilisateur qui utilise Hilt. Cette annotation permet de générer les composants Hilt pour chaque test.
Vous devez également ajouter HiltAndroidRule
à la classe de test. Il gère l'état des composants et permet d'effectuer une injection sur votre test :
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. }
Ensuite, votre test doit connaître la classe Application
générée automatiquement par Hilt.
Tester l'application
Vous devez exécuter les tests d'instrumentation qui utilisent Hilt dans un objet Application
compatible avec Hilt. La bibliothèque fournit HiltTestApplication
à utiliser dans les tests.
Si vos tests nécessitent une autre application de base, consultez la section Application personnalisée pour les tests.
Vous devez configurer votre application de test pour qu'elle s'exécute dans vos tests d'instrumentation ou Robolectric. Les instructions suivantes ne sont pas spécifiques à Hilt, mais sont des consignes générales pour spécifier une application personnalisée à exécuter dans les tests.
Définir l'application de test dans les tests d'instrumentation
Pour utiliser l'application de test Hilt dans les tests d'instrumentation, vous devez configurer un nouveau lanceur de test. Ce lanceur permet à Hilt de fonctionner pour tous les tests d'instrumentation de votre projet. Procédez comme suit :
- Créez une classe personnalisée qui étend
AndroidJUnitRunner
dans le dossierandroidTest
. - Ignorez la fonction
newApplication
et indiquez le nom de l'application de test Hilt générée.
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); } }
Configurez ensuite ce lanceur de test dans votre fichier Gradle, comme décrit dans le guide de tests unitaires d'instrumentation. Veillez à utiliser le chemin de classe complet :
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" } }
Définir l'application de test dans les tests Robolectric
Si vous utilisez Robolectric pour tester votre couche d'interface utilisateur, vous pouvez spécifier l'application à utiliser dans le fichier robolectric.properties
:
application = dagger.hilt.android.testing.HiltTestApplication
Vous pouvez également configurer l'application sur chaque test individuellement à l'aide de l'annotation @Config
de 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. }
Si vous utilisez une version du plug-in Android Gradle antérieure à la version 4.2, activez la transformation des classes @AndroidEntryPoint
dans les tests unitaires locaux en appliquant la configuration suivante dans le fichier build.gradle
de votre module :
Groovy
hilt { enableTransformForLocalTests = true }
Kotlin
hilt { enableTransformForLocalTests = true }
Pour en savoir plus sur enableTransformForLocalTests
, consultez la documentation Hilt.
Fonctionnalités de test
Une fois que Hilt est prêt à être utilisé dans vos tests, vous pouvez personnaliser plusieurs processus à l'aide de plusieurs fonctionnalités.
Injecter des types dans les tests
Pour injecter des types dans un test, utilisez @Inject
pour l'injection de champs. Pour indiquer à Hilt de renseigner les champs @Inject
, appelez hiltRule.inject()
.
Consultez l'exemple suivant d'un test d'instrumentation :
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. } }
Remplacer une liaison
Si vous devez injecter une instance fausse ou factice d'une dépendance, vous devez indiquer à Hilt de ne pas utiliser la liaison utilisée dans le code de production et d'en utiliser une autre. Pour remplacer une liaison, vous devez remplacer le module qui contient la liaison par un module de test contenant les liaisons que vous souhaitez utiliser dans le test.
Par exemple, supposons que votre code de production déclare une liaison pour AnalyticsService
comme suit :
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 ); }
Pour remplacer la liaison AnalyticsService
dans les tests, créez un module Hilt dans le dossier test
ou androidTest
avec la fausse dépendance et annotez-le avec @TestInstallIn
. Tous les tests de ce dossier sont injectés avec une fausse dépendance.
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 ); }
Remplacer une liaison dans un seul test
Pour remplacer une liaison dans un seul test plutôt que dans tous, désinstallez un module Hilt d'un test à l'aide de l'annotation @UninstallModules
et créez un module de test dans le test.
En suivant l'exemple AnalyticsService
de la version précédente, commencez par indiquer à Hilt d'ignorer le module de production en utilisant l'annotation @UninstallModules
dans la classe de test :
Kotlin
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsActivityTest { ... }
Java
@UninstallModules(AnalyticsModule.class) @HiltAndroidTest public final class SettingsActivityTest { ... }
Vous devez ensuite remplacer la liaison. Créez un module dans la classe de test qui définit la liaison de test :
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 ); } ... }
Cela ne remplace la liaison que pour une seule classe de test. Si vous souhaitez remplacer la liaison pour toutes les classes de test, utilisez l'annotation @TestInstallIn
de la section ci-dessus. Vous pouvez également placer la liaison de test dans le module test
pour les tests Robolectric ou dans le module androidTest
pour les tests d'instrumentation.
Nous vous recommandons d'utiliser @TestInstallIn
dans la mesure du possible.
Lier de nouvelles valeurs
Utilisez l'annotation @BindValue
pour lier facilement des champs de votre test au graphique de dépendance de Hilt. Annotez un champ avec @BindValue
. Il est lié sous le type de champ déclaré avec tous les qualificatifs présents pour ce champ.
Dans l'exemple AnalyticsService
, vous pouvez remplacer le service AnalyticsService
par un faux à l'aide de @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(); ... }
Cela simplifie le remplacement d'une liaison et le référencement d'une liaison dans votre test en vous permettant d'effectuer les deux en même temps.
@BindValue
fonctionne avec des qualificatifs et d'autres annotations de test. Par exemple, si vous utilisez des bibliothèques de test telles que Mockito, vous pouvez les utiliser dans un test Robolectric comme suit :
Kotlin
... class SettingsActivityTest { ... @BindValue @ExampleQualifier @Mock lateinit var qualifiedVariable: ExampleCustomType // Robolectric tests here }
Java
... class SettingsActivityTest { ... @BindValue @ExampleQualifier @Mock ExampleCustomType qualifiedVariable; // Robolectric tests here }
Si vous devez ajouter une liaison multiple, vous pouvez utiliser les annotations @BindValueIntoSet
et @BindValueIntoMap
à la place de @BindValue
. @BindValueIntoMap
nécessite que vous annotiez le champ avec une annotation de clé de carte.
Cas particuliers
Hilt fournit également des fonctionnalités adaptées aux cas d'utilisation non standards.
Application personnalisée pour les tests
Si vous ne pouvez pas utiliser HiltTestApplication
, car votre application de test doit étendre une autre application, annotez une nouvelle classe ou interface avec @CustomTestApplication
, en indiquant la valeur de la classe de base que l'application Hilt générée doit étendre.
@CustomTestApplication
génère une classe Application
prête à être testée avec Hilt, qui étend l'application que vous avez transmise en tant que paramètre.
Kotlin
@CustomTestApplication(BaseApplication::class) interface HiltTestApplication
Java
@CustomTestApplication(BaseApplication.class) interface HiltTestApplication { }
Dans cet exemple, Hilt génère une Application
nommée HiltTestApplication_Application
qui étend la classe BaseApplication
. En général, le nom de l'application générée est le nom de la classe annotée, suivie du suffixe _Application
. Vous devez configurer l'application de test générée par Hilt pour qu'elle s'exécute dans vos tests d'instrumentation ou tests Robolectric comme décrit dans la section Tester l'application.
Plusieurs objets TestRule dans votre test d'instrumentation
Si vous avez d'autres objets TestRule
dans votre test, il existe plusieurs façons de s'assurer que toutes les règles fonctionnent ensemble.
Vous pouvez encapsuler les règles comme suit :
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. }
Vous pouvez également utiliser les deux règles au même niveau tant que HiltAndroidRule
s'exécute la première. Spécifiez l'ordre d'exécution à l'aide de l'attribut order
dans l'annotation @Rule
. Cette méthode ne fonctionne qu'à partir de la version 4.13 ou supérieur de JUnit :
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
Il n'est pas possible d'utiliser launchFragmentInContainer
à partir de la bibliothèque androidx.fragment:fragment-testing
avec Hilt, car il repose sur une activité qui n'est pas annotée avec @AndroidEntryPoint
.
Utilisez plutôt le code launchFragmentInHiltContainer
du dépôt GitHub architecture-samples
.
Utiliser un point d'entrée avant que le composant singleton ne soit disponible
L'annotation @EarlyEntryPoint
fournit un mécanisme de sortie lorsqu'un point d'entrée Hilt doit être créé avant que le composant singleton ne soit disponible dans un test Hilt.
Pour en savoir plus sur @EarlyEntryPoint
, consultez la documentation Hilt.