Hilt Testing Guide - Android Developers
Hilt Testing Guide - Android Developers
One of the bene�ts of using dependency injection frameworks like Hilt is that it makes
testing your code easier.
Hilt isn't necessary for unit tests, since when testing a class that uses constructor
injection, you don't need to use Hilt to instantiate that class. Instead, you can directly call
a class constructor by passing in fake or mock dependencies, just as you would if the
constructor weren't annotated:
(#java)
(#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(...)
}
}
For integration tests, Hilt injects dependencies as it would in your production code.
Testing with Hilt requires no maintenance because Hilt automatically generates a new set
of components for each test.
1 of 12 11/6/24, 22:54
Hilt testing guide | Android Developers https://developer.android.com/training/dependency-inject...
(#groovy)
(#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"
You must annotate any UI test that uses Hilt with . This annotation is
responsible for generating the Hilt components for each test.
Also, you need to add the to the test class. It manages the
components' state and is used to pe�orm injection on your test:
(#java)
(#kotlin)
@HiltAndroidTest
2 of 12 11/6/24, 22:54
Hilt testing guide | Android Developers https://developer.android.com/training/dependency-inject...
class SettingsActivityTest {
@get:Rule
var hiltRule = HiltAndroidRule(this)
∕∕ UI tests here.
}
Note: If you have other rules in your test, see Multiple TestRule objects in your instrumented test
(#multiple-testrules).
Next, your test needs to know about the class that Hilt automatically
generates for you.
You must execute instrumented tests that use Hilt in an object that
suppo�s Hilt. The library provides for use in tests. If your tests
need a di�erent base application, see Custom application for tests (#custom-application).
You must set your test application to run in your instrumented tests
(/training/testing/ui-testing) or Robolectric tests (h�p://robolectric.org/). The following
instructions aren't speci�c to Hilt, but are general guidelines on how to specify a custom
application to run in tests.
To use the Hilt test application in instrumented tests (/training/testing/ui-testing), you need
to con�gure a new test runner. This makes Hilt work for all of the instrumented tests in
your project. Pe�orm the following steps:
���Override the function and pass in the name of the generated Hilt
test application.
(#java)
(#kotlin)
3 of 12 11/6/24, 22:54
Hilt testing guide | Android Developers https://developer.android.com/training/dependency-inject...
Next, con�gure this test runner in your Gradle �le as described in the instrumented unit
test guide (/training/testing/unit-testing/instrumented-unit-tests#setup). Make sure you use the
full classpath:
(#groovy)
(#kotlin)
android {
defaultConfig {
∕∕ Replace com.example.android.dagger with your class path.
testInstrumentationRunner = "com.example.android.dagger.CustomTestRunner"
}
}
If you use Robolectric to test your UI layer, you can specify which application to use in the
�le:
Alternatively, you can con�gure the application on each test individually by using
Robolectric's annotation:
(#java)
(#kotlin)
@HiltAndroidTest
@Config(application = HiltTestApplication::class)
class SettingsActivityTest {
@get:Rule
4 of 12 11/6/24, 22:54
Hilt testing guide | Android Developers https://developer.android.com/training/dependency-inject...
If you use an Android Gradle Plugin version lower than 4.2, enable transforming
classes in local unit tests by applying the following con�guration in
your module's �le:
(#groovy)
(#kotlin)
hilt {
enableTransformForLocalTests = true
}
Once Hilt is ready to use in your tests, you can use several features to customize the
testing process.
To inject types into a test, use for �eld injection. To tell Hilt to populate the
�elds, call .
(#java)
(#kotlin)
@HiltAndroidTest
class SettingsActivityTest {
@get:Rule
var hiltRule = HiltAndroidRule(this)
5 of 12 11/6/24, 22:54
Hilt testing guide | Android Developers https://developer.android.com/training/dependency-inject...
@Inject
lateinit var analyticsAdapter: AnalyticsAdapter
@Before
fun init() {
hiltRule.inject()
}
@Test
fun `happy path`() {
∕∕ Can already use analyticsAdapter here.
}
}
If you need to inject a fake or mock instance of a dependency, you need to tell Hilt not to
use the binding that it used in production code and to use a di�erent one instead. To
replace a binding, you need to replace the module that contains the binding with a test
module that contains the bindings that you want to use in the test.
(#java)
(#kotlin)
@Module
@InstallIn(SingletonComponent::class)
abstract class AnalyticsModule {
@Singleton
@Binds
abstract fun bindAnalyticsService(
analyticsServiceImpl: AnalyticsServiceImpl
): AnalyticsService
}
6 of 12 11/6/24, 22:54
Hilt testing guide | Android Developers https://developer.android.com/training/dependency-inject...
(#java)
(#kotlin)
@Module
@TestInstallIn(
components = [SingletonComponent::class],
replaces = [AnalyticsModule::class]
)
abstract class FakeAnalyticsModule {
@Singleton
@Binds
abstract fun bindAnalyticsService(
fakeAnalyticsService: FakeAnalyticsService
): AnalyticsService
}
To replace a binding in a single test instead of all tests, uninstall a Hilt module from a test
using the annotation and create a new test module inside the test.
Following the example from the previous version, begin by telling Hilt
to ignore the production module by using the annotation in the test
class:
(#java)
(#kotlin)
@UninstallModules(AnalyticsModule::class)
@HiltAndroidTest
class SettingsActivityTest { ... }
Next, you must replace the binding. Create a new module within the test class that
de�nes the test binding:
(#java)
(#kotlin)
@UninstallModules(AnalyticsModule::class)
7 of 12 11/6/24, 22:54
Hilt testing guide | Android Developers https://developer.android.com/training/dependency-inject...
@HiltAndroidTest
class SettingsActivityTest {
@Module
@InstallIn(SingletonComponent::class)
abstract class TestModule {
@Singleton
@Binds
abstract fun bindAnalyticsService(
fakeAnalyticsService: FakeAnalyticsService
): AnalyticsService
}
...
}
This only replaces the binding for a single test class. If you want to replace the binding for
all test classes, use the annotation from the section above.
Alternatively, you can put the test binding in the module for Robolectric tests, or in
the module for instrumented tests. The recommendation is to use
whenever possible.
Warning: You cannot uninstall modules that are not annotated with . A�empting to do so
causes a compilation error.
Note: As Hilt creates new components for tests that use , it can signi�cantly
impact unit test build times. Use it when necessary and prefer using when the
bindings need to be replaced in all test classes.
Use the annotation to easily bind �elds in your test into the Hilt dependency
graph. Annotate a �eld with and it will be bound under the declared �eld
8 of 12 11/6/24, 22:54
Hilt testing guide | Android Developers https://developer.android.com/training/dependency-inject...
type with any quali�ers that are present for that �eld.
(#java)
(#kotlin)
@UninstallModules(AnalyticsModule::class)
@HiltAndroidTest
class SettingsActivityTest {
@BindValue @JvmField
val analyticsService: AnalyticsService = FakeAnalyticsService()
...
}
This simpli�es both replacing a binding and referencing a binding in your test by allowing
you to do both at the same time.
works with quali�ers and other testing annotations. For example, if you use
testing libraries such as Mockito (h�ps://site.mockito.org/), you could use it in a Robolectric
test as follows:
(#java)
(#kotlin)
...
class SettingsActivityTest {
...
9 of 12 11/6/24, 22:54
Hilt testing guide | Android Developers https://developer.android.com/training/dependency-inject...
(#java)
(#kotlin)
@CustomTestApplication(BaseApplication::class)
interface HiltTestApplication
If you have other objects in your test, there are multiple ways to ensure that all
of the rules work together.
(#java)
(#kotlin)
10 of 12 11/6/24, 22:54
Hilt testing guide | Android Developers https://developer.android.com/training/dependency-inject...
@HiltAndroidTest
class SettingsActivityTest {
@get:Rule
var rule = RuleChain.outerRule(HiltAndroidRule(this)).
around(SettingsActivityTestRule(...))
∕∕ UI tests here.
}
Alternatively, you can use both rules at the same level as long as the
executes �rst. Specify the execution order using the a�ribute in the
annotation. This only works in JUnit version 4.13 or higher:
(#java)
(#kotlin)
@HiltAndroidTest
class SettingsActivityTest {
@get:Rule(order = 0)
var hiltRule = HiltAndroidRule(this)
@get:Rule(order = 1)
var settingsActivityTestRule = SettingsActivityTestRule(...)
∕∕ UI tests here.
}
Use the
(h�ps://github.com/android/architecture-samples/blob/views-hilt/app/src/androidTest/java/com/
example/android/architecture/blueprints/todoapp/HiltExt.kt#L37)
code from the (h�ps://github.com/android/architecture-samples)
GitHub repository instead.
11 of 12 11/6/24, 22:54
Hilt testing guide | Android Developers https://developer.android.com/training/dependency-inject...
Content and code samples on this page are subject to the licenses described in the Content License
(/license). Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its a�liates.
12 of 12 11/6/24, 22:54