MyFamilyTracker
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Hilt makes dependency injection elegant in production code. In tests, it provides HiltAndroidRule and test modules that let you swap out real implementations for fakes. Here's how to use them correctly.
On this page
Hilt solves dependency injection for production code. Test DI is a separate problem — and Hilt solves that too, with a different set of tools.
Here's how to wire up Hilt for testing so your test doubles (fakes, mocks) are injected correctly.
Add the Hilt testing dependency:
// build.gradle.kts
androidTestImplementation("com.google.dagger:hilt-android-testing:2.51")
kaptAndroidTest("com.google.dagger:hilt-android-compiler:2.51")
testImplementation("com.google.dagger:hilt-android-testing:2.51")
kaptTest("com.google.dagger:hilt-android-compiler:2.51")Every Hilt instrumentation test needs two things:
@HiltAndroidTest
class TaskScreenTest {
@get:Rule(order = 0)
val hiltRule = HiltAndroidRule(this)
@get:Rule(order = 1)
val composeRule = createAndroidComposeRule<HiltTestActivity>()
@Inject
lateinit var taskRepository: TaskRepository // Injected by Hilt
@Before
fun setup() {
hiltRule.inject()
}
}HiltAndroidRulehiltRule.inject()@InjectCreate a test module that overrides your production module:
// Production module
@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {
@Provides
@Singleton
fun provideTaskRepository(dao: TaskDao): TaskRepository =
TaskRepositoryImpl(dao)
}
// Test module — replaces RepositoryModule in tests
@TestInstallIn(
components = [SingletonComponent::class],
replaces = [RepositoryModule::class]
)
@Module
object FakeRepositoryModule {
@Provides
@Singleton
fun provideTaskRepository(): TaskRepository = FakeTaskRepository()
}The
@TestInstallInRepositoryModuleSometimes you want to inject specific instances per test, not globally:
@HiltAndroidTest
class TaskViewModelTest {
@get:Rule
val hiltRule = HiltAndroidRule(this)
@BindValue
@JvmField
val taskRepository: TaskRepository = FakeTaskRepository()
// taskRepository is now what Hilt injects for TaskRepository
@Test
fun `loading tasks shows them in UI`() {
(taskRepository as FakeTaskRepository).addTask(
Task("1", "Buy milk", false)
)
// ... test code
}
}@BindValueFor unit tests (pure JVM, no Android framework), Hilt isn't needed. Inject dependencies manually:
// No Hilt annotation needed
class TaskViewModelTest {
private val fakeRepository = FakeTaskRepository()
private val viewModel = TaskViewModel(repository = fakeRepository)
@Test
fun `empty state when no tasks`() = runTest {
val state = viewModel.uiState.value
assertTrue(state.tasks.isEmpty())
}
}This is faster and simpler. Reserve
@HiltAndroidTestHilt instrumentation tests require
HiltTestActivity// debug/AndroidManifest.xml
<activity
android:name="dagger.hilt.android.testing.HiltTestActivity"
android:exported="false" />When testing Compose with Hilt:
@get:Rule(order = 1)
val composeRule = createAndroidComposeRule<HiltTestActivity>()If your ViewModel is injected via
@HiltViewModel@HiltAndroidTest
class TaskViewModelHiltTest {
@get:Rule(order = 0)
val hiltRule = HiltAndroidRule(this)
@get:Rule(order = 1)
val composeRule = createAndroidComposeRule<HiltTestActivity>()
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private lateinit var viewModel: TaskViewModel
@Before
fun setup() {
hiltRule.inject()
viewModel = ViewModelProvider(composeRule.activity, viewModelFactory)[TaskViewModel::class.java]
}
@Test
fun `viewmodel loaded with injected fake repository`() = runTest {
// viewModel now uses FakeTaskRepository from FakeRepositoryModule
val state = viewModel.uiState.value
assertNotNull(state)
}
}Fakes (manual implementations of interfaces):
class FakeTaskRepository : TaskRepository {
private val tasks = mutableListOf<Task>()
override suspend fun createTask(task: Task) { tasks.add(task) }
override suspend fun getAllTasks() = tasks.toList()
fun addTask(task: Task) = tasks.add(task) // Test helper
fun clear() = tasks.clear()
}Mocks (MockK or Mockito):
For Hilt tests that run across many test classes, fakes are preferable. They're simpler to maintain than mock configurations.
@HiltAndroidTestHiltAndroidRule@TestInstallIn@BindValueSudarshan Chaudhari
AI Systems Builder / Product Engineer
Bangkok, Thailand
Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.
Related Posts
Related Apps
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Building something? Available for Android dev and QA consulting.
Work with meComments — powered by Giscus
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
ReadPrivate dream journal — structured entry capture, pattern tagging, and optional Claude-powered insight generation. All data stays on-device by default.
ReadWorkout tracker — exercise logging with set/rep/weight history, goal progression, and local Room DB persistence. No account, no cloud sync required.
Read