Um dos benefícios de usar estruturas de injeção de dependência como o Hilt é que ele facilita o teste do código.
Testes de unidade
O Hilt não é necessário para testes de unidade. Ao testar uma classe que usa a injeção de construtor, você não precisa usar o Hilt para instanciar essa classe. Em vez disso, você pode chamar um construtor de classe diretamente passando dependências falsas ou simuladas, assim como faria se o construtor não fosse anotado:
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(...); } }
Testes de ponta a ponta
Para testes de integração, o Hilt injeta dependências como faria no código de produção. O teste com o Hilt não requer manutenção porque ele gera automaticamente um novo conjunto de componentes para cada teste.
Adicionar dependências de teste
Para usar o Hilt nos testes, inclua a dependência hilt-android-testing
no
seu projeto:
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") }
Configuração de teste da IU
Anote qualquer teste de IU que use o Hilt com @HiltAndroidTest
. Essa
anotação é responsável por gerar os componentes do Hilt para cada teste.
Além disso, é necessário adicionar o HiltAndroidRule
à classe de teste. Ele gerencia o
estado dos componentes e é usado para realizar a injeção no teste:
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. }
Em seguida, seu teste precisa saber sobre a classe Application
que o Hilt
gera automaticamente para você.
Aplicativo de teste
Execute testes instrumentados que usam o Hilt em um objeto Application
com suporte. A biblioteca fornece o HiltTestApplication
para uso em testes.
Se os testes precisarem de um aplicativo base diferente, consulte Aplicativo personalizado para
testes.
Execute seu aplicativo de testes nos testes de instrumentação ou testes do Robolectric. As instruções a seguir não são específicas para o Hilt, mas são diretrizes gerais sobre como especificar um aplicativo personalizado para execução em testes.
Definir o aplicativo de teste em testes de instrumentação
Para usar o aplicativo de teste do Hilt em testes de instrumentação, configure um novo executor de teste. Isso faz com que o Hilt funcione para todos os testes de instrumentação no seu projeto. Siga as etapas abaixo:
- Crie uma classe personalizada que estenda
AndroidJUnitRunner
na pastaandroidTest
. - Modifique a função
newApplication
e transmita o nome do aplicativo de teste Hilt gerado.
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); } }
Em seguida, configure esse executor de testes no arquivo Gradle, conforme descrito no guia de teste de unidade instrumentado. Use o caminho de classe completo:
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" } }
Definir o aplicativo de teste em testes do Robolectric
Se você usa o Roboletric para testar sua camada de IU, pode especificar qual aplicativo
usar no arquivo robolectric.properties
:
application = dagger.hilt.android.testing.HiltTestApplication
Como alternativa, é possível configurar o aplicativo em cada teste individualmente
usando a anotação @Config
do 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 você usa uma versão do Plug-in do Android para Gradle anterior à 4.2, ative
a transformação de classes @AndroidEntryPoint
em testes de unidade locais aplicando a
seguinte configuração no arquivo build.gradle
do módulo:
Groovy
hilt { enableTransformForLocalTests = true }
Kotlin
hilt { enableTransformForLocalTests = true }
Veja mais informações sobre enableTransformForLocalTests
na documentação do
Hilt.
Recursos de teste
Quando o Hilt estiver pronto para ser usado nos testes, você poderá usar vários recursos para personalizar esse processo.
Injetar tipos em testes
Para injetar tipos em um teste, use @Inject
para injeção de campo. Para instruir o Hilt a
preencher os campos @Inject
, chame hiltRule.inject()
.
Veja um exemplo de teste de instrumentação:
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. } }
Substituir uma vinculação
Se você precisar injetar uma instância falsa ou simulada de uma dependência, será necessário instruir o Hilt a não usar a vinculação do código de produção, mas sim uma diferente. Para substituir uma vinculação, é necessário substituir o módulo que contém a vinculação por um módulo de teste que contenha as vinculações que você quer usar no teste.
Por exemplo, suponha que seu código de produção declare uma vinculação para
AnalyticsService
da seguinte maneira:
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 ); }
Para substituir a vinculação AnalyticsService
nos testes, crie um novo módulo Hilt na
pasta test
ou androidTest
com a dependência falsa e inclua uma anotação
@TestInstallIn
. Todos os testes nessa pasta vão ser injetados com a dependência
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 ); }
Substituir uma vinculação em um único teste
Para substituir uma vinculação em um único teste em vez de todos os testes, desinstale um módulo
do Hilt de um teste usando a anotação @UninstallModules
e crie um novo
dentro do teste.
Seguindo o exemplo AnalyticsService
da versão anterior, comece pedindo ao
Hilt para ignorar o módulo de produção usando a anotação @UninstallModules
na classe de teste:
Kotlin
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsActivityTest { ... }
Java
@UninstallModules(AnalyticsModule.class) @HiltAndroidTest public final class SettingsActivityTest { ... }
Em seguida, substitua a vinculação. Crie um novo módulo na classe de teste que defina a vinculação:
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 ); } ... }
Isso substitui apenas a vinculação de uma única classe de teste. Para substituir
a vinculação de todas as classes de teste, use a anotação @TestInstallIn
da
seção acima. Como alternativa, você pode colocar a vinculação de teste no módulo test
para testes do Robolectric ou no módulo androidTest
para testes de instrumentação.
Recomendamos usar @TestInstallIn
sempre que possível.
Vincular novos valores
Use a anotação @BindValue
para vincular facilmente campos do seu teste ao gráfico
de dependência do Hilt. Anote um campo com @BindValue
e ele será vinculado ao
tipo de campo declarado com qualquer qualificador presente nesse campo.
No exemplo AnalyticsService
, você pode substituir um
AnalyticsService
por um falso usando @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(); ... }
Isso simplifica a substituição de uma vinculação e a referência no teste, permitindo que você faça as duas coisas ao mesmo tempo.
O @BindValue
funciona com qualificador e outras anotações de teste. Por exemplo,
se você usa bibliotecas de teste, como
Mockito, pode usá-las em um
teste do Robolectric da seguinte maneira:
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 você precisar adicionar uma multivinculação,
use as anotações @BindValueIntoSet
e @BindValueIntoMap
no lugar
de @BindValue
. @BindValueIntoMap
requer que você também anote o campo
com uma anotação de chave de mapa.
Casos especiais
O Hilt também oferece recursos compatíveis com casos de uso não padrão.
Aplicativo personalizado para testes
Se não for possível usar HiltTestApplication
porque seu aplicativo de teste precisa
estender outro app, adicione a anotação
@CustomTestApplication
a uma nova classe ou interface e transmita o valor da classe de base que
será estendida pelo Hilt gerado.
Com o Hilt, o @CustomTestApplication
vai gerar uma classe Application
pronta para testes
que estende o aplicativo transmitido como parâmetro.
Kotlin
@CustomTestApplication(BaseApplication::class) interface HiltTestApplication
Java
@CustomTestApplication(BaseApplication.class) interface HiltTestApplication { }
No exemplo, o Hilt gera um Application
chamado
HiltTestApplication_Application
que estende a classe BaseApplication
. Em
geral, o nome do aplicativo gerado é o nome da classe
anotada acrescida de _Application
. Defina o aplicativo de teste do Hilt
gerado para ser executado nos seus testes de instrumentação ou
testes do Robolectric, conforme descrito em Aplicativo
de teste.
Vários objetos TestRule no teste de instrumentação
Se você tiver outros objetos TestRule
no teste, há várias maneiras de
garantir que todas as regras funcionem juntas.
É possível unir as regras da seguinte maneira:
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. }
Como alternativa, você pode usar as duas regras no mesmo nível, desde que a
HiltAndroidRule
seja executada primeiro. Especifique a ordem de execução usando o
atributo order
na anotação @Rule
. Isso funciona apenas na versão 4.13 ou mais recente
do 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
Não é possível usar o launchFragmentInContainer
da
biblioteca androidx.fragment:fragment-testing
com o Hilt, porque ele depende de uma
atividade que não tem a anotação @AndroidEntryPoint
.
Use o
código launchFragmentInHiltContainer
do repositório
architecture-samples
do
GitHub.
Usar um ponto de entrada antes que o componente singleton esteja disponível
A anotação @EarlyEntryPoint
fornece uma saída de emergência quando um ponto de entrada
do Hilt precisa ser criado antes que o componente singleton esteja disponível em um
teste do Hilt.
Veja mais informações sobre @EarlyEntryPoint
na
documentação do Hilt.