Uno dei vantaggi dell'utilizzo di framework di dipendenza come Hilt è che semplifica il test del codice.
Test delle unità
Hilt non è necessario per i test delle unità, in quanto durante il test di una classe che utilizza costruttore, non devi usare Hilt per creare un'istanza di quella classe. Invece, puoi chiamare direttamente un costruttore di classe passando un testo falso o fittizio 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 nella tua produzione le API nel tuo codice. Il test con Hilt non richiede manutenzione perché Hilt automaticamente genera un nuovo insieme di componenti per ogni test.
Aggiunta di dipendenze di test
Per utilizzare Hilt nei tuoi test, includi la dipendenza hilt-android-testing
nel tuo
progetto:
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") }
Configurazione del test della UI
Devi annotare qualsiasi test dell'interfaccia utente che utilizza Hilt con @HiltAndroidTest
. Questo
è responsabile della generazione dei componenti Hilt per ogni test.
Inoltre, devi aggiungere HiltAndroidRule
alla lezione di prova. Gestisce lo stato dei componenti e viene utilizzato per eseguire l'iniezione 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. }
Il prossimo test deve conoscere la classe Application
che Hilt
viene generata automaticamente per te.
Testare l'applicazione
Devi eseguire test con strumenti che utilizzano Hilt in un oggetto Application
che supporta Hilt. La libreria fornisce HiltTestApplication
da utilizzare per i test.
Se i test richiedono un'applicazione di base diversa, consulta Applicazione personalizzata per
test.
Devi impostare l'applicazione di test in modo che venga eseguita nel tuo strumento test o Robolectric test. Le seguenti istruzioni non sono specifiche di Hilt, ma sono linee guida generali su come specificare per l'esecuzione nei test.
Imposta l'applicazione di test in test instrumentati
Per utilizzare l'applicazione di test Hilt in instrumented test, devi configurare un nuovo runner del test. In questo modo Hilt può funzionare per tutti i test strumentati nel tuo progetto. Procedi come indicato di seguito:
- Crea una classe personalizzata che si estende
AndroidJUnitRunner
pollici nella cartellaandroidTest
. - Esegui l'override della funzione
newApplication
e inserisci il nome del cluster generato Applicazione di test Hilt.
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); } }
Configura quindi questo runner di test nel file Gradle come descritto in test delle unità strumentate . Assicurati che utilizza l'intero classpath:
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" } }
Impostare l'applicazione di test nei test robotlettici
Se utilizzi Robolectric per testare il livello UI, puoi specificare quale applicazione
da utilizzare nel file robolectric.properties
:
application = dagger.hilt.android.testing.HiltTestApplication
In alternativa, puoi configurare l'applicazione singolarmente per ogni test
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 Gradle precedente alla 4.2, attiva
trasformare le classi @AndroidEntryPoint
nei test delle unità locali applicando
seguente configurazione nel file build.gradle
del modulo:
Groovy
hilt { enableTransformForLocalTests = true }
Kotlin
hilt { enableTransformForLocalTests = true }
Ulteriori informazioni su enableTransformForLocalTests
in Hilt
documentazione.
Funzionalità di test
Quando Hilt è pronto per essere utilizzato nei test, puoi utilizzare diverse funzionalità per personalizzare il processo di test.
Esegui l'iniezione di tipi nei test
Per inserire i tipi in un test, utilizza @Inject
per l'inserimento del campo. Per indicare a Hilt di
compila i campi @Inject
, richiama hiltRule.inject()
.
Vedi il seguente esempio di test instrumentato:
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. } }
Sostituisci un'associazione
Se devi inserire un'istanza falsa o fittizia di una dipendenza, devi Non puoi utilizzare l'associazione utilizzata nel codice di produzione e un'altra. Per sostituire un'associazione, devi sostituire il modulo contiene l'associazione con un modulo di test che contiene le associazioni che vuoi da utilizzare nel test.
Ad esempio, supponiamo che il codice di produzione dichiari una 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 in
la cartella test
o androidTest
con la dipendenza falsa e annotarla
con @TestInstallIn
. Tutti i test nella cartella vengono inseriti con il falso
la dipendenza.
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 Hilt
modulo da un test utilizzando l'annotazione @UninstallModules
e crea un nuovo
all'interno del test.
Seguendo l'esempio AnalyticsService
della versione precedente, inizia dicendo
Puoi 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 { ... }
Devi quindi sostituire l'associazione. Crea un nuovo modulo nella classe di test che definisce 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 ); } ... }
In questo modo viene sostituita solo l'associazione per una singola classe di test. Se vuoi sostituire
l'associazione per tutte le classi di test, utilizza l'annotazione @TestInstallIn
del
sezione precedente. In alternativa, puoi inserire l'associazione di test nel modulo test
per i test robotici o nel modulo androidTest
per i test instrumentati.
È consigliabile utilizzare @TestInstallIn
quando possibile.
Associazione di nuovi valori
Utilizza l'annotazione @BindValue
per associare facilmente i campi del test a Hilt
un grafico delle dipendenze. Annota un campo con @BindValue
e verrà associato in
il tipo di campo dichiarato con 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 sia la sostituzione di un'associazione sia il riferimento a un'associazione nel test consentendoti di eseguire entrambe le operazioni contemporaneamente.
@BindValue
funziona con i qualificatori e altre annotazioni di test. Ad esempio:
se utilizzi librerie di test come
Mockito, potresti usarlo in una
Il test robotico è il seguente:
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 un'associazione multipla,
puoi utilizzare le annotazioni @BindValueIntoSet
e @BindValueIntoMap
di @BindValue
. @BindValueIntoMap
richiede anche di annotare il campo
con un'annotazione relativa alla chiave di mappa.
Casi particolari
Hilt offre 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, annotare una nuova classe o interfaccia con
@CustomTestApplication
, passando il valore della classe base che deve
generata l'applicazione Hilt per estenderla.
@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
. Nel
generale, il nome dell'applicazione generata è il nome dell'applicazione
classe aggiunta con _Application
. Devi impostare il test Hilt generato
dell'applicazione da eseguire nei test instrumentati
Test robolectrici come descritto in Test
applicazione.
Più oggetti TestRule nel test instrumentato
Se hai altri oggetti TestRule
nel test, esistono diversi modi per
per assicurarsi che tutte le regole funzionino insieme.
Puoi aggregare le regole nel seguente modo:
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, purché
HiltAndroidRule
viene eseguito per primo. Specifica l'ordine di esecuzione utilizzando il metodo
Attributo order
nell'annotazione @Rule
. Funziona solo nella versione JUnit
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
dal
androidx.fragment:fragment-testing
con Hilt, perché si basa su un
attività non annotate con @AndroidEntryPoint
.
Utilizza la
launchFragmentInHiltContainer
il codice
architecture-samples
GitHub
repository Git.
Utilizza un punto di ingresso prima che il componente singleton sia disponibile
L'annotazione @EarlyEntryPoint
fornisce un'uscita d'uscita quando un ingresso Hilt
deve essere creato prima che il componente singleton sia disponibile in un
Test Hilt.
Scopri di più su @EarlyEntryPoint
nella
documentazione di Hilt.