Uno dei vantaggi dell'uso di framework di inserimento delle dipendenze come Hilt è che semplifica il test del codice.
Test delle unità
Hilt non è necessario per i test delle unità poiché, quando testi una classe che utilizza l'inserimento del costruttore, non è necessario utilizzare Hilt per creare un'istanza di quella classe. Puoi invece chiamare direttamente un costruttore di classe passando in dipendenze false o fittizie, proprio come faresti se il costruttore non fosse annotato:
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(...); } }
Test end-to-end
Per i test di integrazione, Hilt inserisce le dipendenze come farebbe nel tuo codice di produzione. I test con Hilt non richiedono manutenzione perché Hilt genera automaticamente un nuovo set di componenti per ogni test.
Aggiunta di dipendenze di test
Per utilizzare Hilt nei test, includi la dipendenza hilt-android-testing
nel progetto:
trendy
dependencies { // For Robolectric tests. testImplementation 'com.google.dagger:hilt-android-testing:2.44' // ...with Kotlin. kaptTest 'com.google.dagger:hilt-android-compiler:2.44' // ...with Java. testAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.44' // For instrumented tests. androidTestImplementation 'com.google.dagger:hilt-android-testing:2.44' // ...with Kotlin. kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.44' // ...with Java. androidTestAnnotationProcessor 'com.google.dagger:hilt-android-compiler:2.44' }
Kotlin
dependencies { // For Robolectric tests. testImplementation("com.google.dagger:hilt-android-testing:2.44") // ...with Kotlin. kaptTest("com.google.dagger:hilt-android-compiler:2.44") // ...with Java. testAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.44") // For instrumented tests. androidTestImplementation("com.google.dagger:hilt-android-testing:2.44") // ...with Kotlin. kaptAndroidTest("com.google.dagger:hilt-android-compiler:2.44") // ...with Java. androidTestAnnotationProcessor("com.google.dagger:hilt-android-compiler:2.44") }
Configurazione del test UI
Devi annotare qualsiasi test dell'interfaccia utente che utilizza Hilt con @HiltAndroidTest
. Questa annotazione è responsabile della generazione dei componenti di Hilt per ogni test.
Inoltre, devi aggiungere HiltAndroidRule
al corso di test. Gestisce lo stato dei componenti ed è utilizzato per eseguire l'inserimento nel 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. }
Successivamente, il test deve conoscere la classe Application
che Hilt genera automaticamente per te.
Testa l'applicazione
Devi eseguire test instrumentati che utilizzano Hilt in un oggetto Application
che supporta Hilt. La libreria fornisce HiltTestApplication
da utilizzare nei test.
Se i test richiedono un'applicazione di base diversa, consulta Applicazione personalizzata per i test.
Devi impostare l'applicazione di test in modo che venga eseguita nei test strumentati o nei test robolectrici. Le seguenti istruzioni non sono specifiche per Hilt, ma sono linee guida generali su come specificare un'applicazione personalizzata da eseguire nei test.
Imposta l'applicazione di test nei test strumentati
Per utilizzare l'applicazione Hilt test nei test strumentati, devi configurare un nuovo esecutore di test. Questo fa sì che Hilt funzioni per tutti i test instrumentati nel tuo progetto. Segui questi passaggi:
- Crea una classe personalizzata che si estende
AndroidJUnitRunner
nella cartellaandroidTest
. - Esegui l'override della funzione
newApplication
e passa il nome dell'applicazione di test di Hilt generata.
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); } }
Quindi, configura questo esecutore del test nel file Gradle come descritto nella guida relativa al test delle unità strumentate. Assicurati di utilizzare l'intero classpath:
trendy
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" } }
Imposta l'applicazione di test nei test Robolectric
Se usi Robolectric per testare il tuo livello UI, puoi specificare quale applicazione
utilizzare nel file robolectric.properties
:
application = dagger.hilt.android.testing.HiltTestApplication
In alternativa, puoi configurare l'applicazione per ogni test individualmente utilizzando l'annotazione @Config
di 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. }
Se utilizzi una versione del plug-in Android per Gradle precedente alla 4.2, abilita la trasformazione delle classi @AndroidEntryPoint
nei test delle unità locali applicando la seguente configurazione nel file build.gradle
del modulo:
trendy
hilt { enableTransformForLocalTests = true }
Kotlin
hilt { enableTransformForLocalTests = true }
Scopri di più su enableTransformForLocalTests
nella documentazione di Hilt.
Test delle funzionalità
Quando Hilt è pronto per essere utilizzato nei tuoi test, puoi utilizzare diverse funzionalità per personalizzare il processo di test.
Tipi di inserimento nei test
Per inserire i tipi in un test, utilizza @Inject
per l'inserimento dei campi. Per indicare a Hilt di
compilare i campi @Inject
, chiama hiltRule.inject()
.
Vedi l'esempio seguente di un test strumentato:
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. } }
Sostituire un'associazione
Se devi inserire un'istanza falsa o fittizia di una dipendenza, devi indicare a Hilt di non utilizzare l'associazione che ha utilizzato nel codice di produzione e di utilizzarne una diversa. Per sostituire un'associazione, devi sostituire il modulo contenente l'associazione con un modulo di test contenente le associazioni che vuoi utilizzare nel test.
Ad esempio, supponi che il tuo codice di produzione dichiari un'associazione per AnalyticsService
come segue:
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 ); }
Per sostituire l'associazione AnalyticsService
nei test, crea un nuovo modulo Hilt nella cartella test
o androidTest
con la dipendenza falsa e annotalo con @TestInstallIn
. Tutti i test in quella cartella vengono inseriti con
la dipendenza falsa.
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 ); }
Sostituire un'associazione in un singolo test
Per sostituire un'associazione in un singolo test anziché in tutti i test, disinstalla un modulo Hilt da un test utilizzando l'annotazione @UninstallModules
e crea un nuovo modulo di test all'interno del test.
Segui l'esempio AnalyticsService
della versione precedente, inizia dicendo a Hilt di ignorare il modulo di produzione utilizzando l'annotazione @UninstallModules
nella classe di test:
Kotlin
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsActivityTest { ... }
Java
@UninstallModules(AnalyticsModule.class) @HiltAndroidTest public final class SettingsActivityTest { ... }
Poi, devi sostituire l'associazione. Crea un nuovo modulo all'interno della classe di test che definisca l'associazione di 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 ); } ... }
Sostituisce solo l'associazione per una singola classe di test. Se vuoi sostituire
l'associazione per tutte le classi di test, utilizza l'annotazione @TestInstallIn
della
sezione precedente. In alternativa, puoi inserire l'associazione di test nel modulo test
per i test Robolectric o nel modulo androidTest
per i test instrumentati.
Il consiglio è di utilizzare @TestInstallIn
quando possibile.
Associazione di nuovi valori
Utilizza l'annotazione @BindValue
per associare facilmente i campi del test al grafico delle dipendenze di Hilt. Se aggiungi un valore @BindValue
a un campo, questo verrà associato al tipo di campo dichiarato con tutti i qualificatori presenti per quel campo.
Nell'esempio AnalyticsService
, puoi sostituire AnalyticsService
con
un falso utilizzando @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(); ... }
Ciò semplifica la sostituzione di un'associazione e il riferimento a un'associazione nel test consentendo di eseguire entrambe le operazioni contemporaneamente.
@BindValue
funziona con qualificatori e altre annotazioni di test. Ad esempio,
se utilizzi librerie di test come
Mockito, puoi utilizzarle in un
test robolectric come segue:
Kotlin
... class SettingsActivityTest { ... @BindValue @ExampleQualifier @Mock lateinit var qualifiedVariable: ExampleCustomType // Robolectric tests here }
Java
... class SettingsActivityTest { ... @BindValue @ExampleQualifier @Mock ExampleCustomType qualifiedVariable; // Robolectric tests here }
Se devi aggiungere una multiassociazione,
puoi utilizzare le annotazioni @BindValueIntoSet
e @BindValueIntoMap
al posto di
@BindValue
. @BindValueIntoMap
richiede anche di annotare il campo con un'annotazione della chiave della mappa.
Casi particolari
Hilt fornisce inoltre funzionalità per supportare casi d'uso non standard.
Applicazione personalizzata per i test
Se non puoi utilizzare HiltTestApplication
perché la tua applicazione di test deve estendere un'altra applicazione, annota una nuova classe o interfaccia con @CustomTestApplication
, passando il valore della classe base che vuoi che l'applicazione Hilt generata estenda.
@CustomTestApplication
genererà una classe Application
pronta per il test con Hilt che estende l'applicazione passata come parametro.
Kotlin
@CustomTestApplication(BaseApplication::class) interface HiltTestApplication
Java
@CustomTestApplication(BaseApplication.class) interface HiltTestApplication { }
Nell'esempio, Hilt genera un Application
denominato
HiltTestApplication_Application
che estende la classe BaseApplication
. In generale, il nome dell'applicazione generata è il nome della classe annotata aggiunta con _Application
. Devi impostare l'applicazione di test Hilt generata in modo che venga eseguita nei test instrumentati o nei test robolectrici come descritto nell'applicazione di test.
Più oggetti TestRule nel test instrumentato
Se il test contiene altri oggetti TestRule
, esistono diversi modi per
assicurare che tutte le regole funzionino insieme.
Puoi aggregare le regole come segue:
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. }
In alternativa, puoi utilizzare entrambe le regole allo stesso livello, a condizione che
HiltAndroidRule
venga eseguita prima. Specifica l'ordine di esecuzione utilizzando l'attributo order
nell'annotazione @Rule
. Funziona solo con JUnit
versione 4.13 o successive:
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. }
lancioFragmentInContainer
Non è possibile utilizzare launchFragmentInContainer
dalla libreria androidx.fragment:fragment-testing
con Hilt, perché si basa su un'attività non annotata con @AndroidEntryPoint
.
Utilizza invece il codice launchFragmentInHiltContainer
dal repository GitHub di architecture-samples
.
Utilizza un punto di ingresso prima che il componente singleton sia disponibile
L'annotazione @EarlyEntryPoint
fornisce un'alternativa quando è necessario creare un punto di ingresso di Hilt prima che il componente singleton sia disponibile in un test di Hilt.
Maggiori informazioni su @EarlyEntryPoint
nella
documentazione di Hilt.