Skip to content

Commit d31721f

Browse files
author
Manuel Vivo
authored
Add Hilt to the project (#857)
1 parent 9575ea5 commit d31721f

File tree

35 files changed

+468
-548
lines changed

35 files changed

+468
-548
lines changed

.github/workflows/blueprints.yaml

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,35 +35,29 @@ jobs:
3535
~/.gradle/caches/build-cache-*
3636
key: gradle-${{ hashFiles('checksum.txt') }}
3737

38-
- name: Build project Mock and UnitTest
39-
run: ./gradlew assembleMockDebug assembleProdDebug compileMockDebugUnitTestKotlin
38+
- name: Build project and UnitTest
39+
run: ./gradlew assembleDebug compileDebugUnitTestKotlin
4040

4141
- name: Spotless
4242
run: ./gradlew spotlessCheck
4343

44-
- name: Robolectric Mock
45-
run: ./gradlew testMockDebugUnitTest
46-
47-
- name: Build project Prod and UnitTest
48-
run: ./gradlew assembleMockDebug assembleProdDebug compileProdDebugUnitTestKotlin
49-
50-
- name: Robolectric Prod
51-
run: ./gradlew spotlessCheck assembleMockDebug assembleProdDebug testMockDebugUnitTest testProdDebugUnitTest --stacktrace
44+
- name: Robolectric
45+
run: ./gradlew testDebugUnitTest
5246

5347
# Needed to accept licenses
5448
- name: Setup Android SDK
5549
uses: android-actions/setup-android@v2
5650

57-
- name: Compile AndroidTests Mock
58-
run: ./gradlew compileMockDebugAndroidTestKotlin
51+
- name: Compile AndroidTests
52+
run: ./gradlew compileDebugAndroidTestKotlin
5953

6054
- name: Run all tests pixel 2 api 30 ATD
6155
working-directory: .
62-
run: ./gradlew -Pandroid.sdk.channel=3 -Pandroid.experimental.androidTest.numManagedDeviceShards=1 -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" pixel2api30atdMockDebugAndroidTest
56+
run: ./gradlew -Pandroid.sdk.channel=3 -Pandroid.experimental.androidTest.numManagedDeviceShards=1 -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" pixel2api30atdDebugAndroidTest
6357

6458
- name: Run all tests pixel 2 api 30 non-ATD
6559
working-directory: .
66-
run: ./gradlew -Pandroid.sdk.channel=3 -Pandroid.experimental.androidTest.numManagedDeviceShards=1 -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" pixel2api30MockDebugAndroidTest
60+
run: ./gradlew -Pandroid.sdk.channel=3 -Pandroid.experimental.androidTest.numManagedDeviceShards=1 -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" pixel2api30DebugAndroidTest
6761

6862
- name: Upload build reports
6963
if: always()

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ In this branch you'll find:
1313
* A **data layer** with a repository and two data sources (local using Room and a fake remote).
1414
* Two **product flavors**, `mock` and `prod`, [to ease development and testing](https://android-developers.googleblog.com/2015/12/leveraging-product-flavors-in-android.html).
1515
* A collection of unit, integration and e2e **tests**, including "shared" tests that can be run on emulator/device or Robolectric.
16-
* A simple service locator for inversion of control.
16+
* Dependency injection using [Hilt](https://developer.android.com/training/dependency-injection/hilt-android).
1717

1818
## Variations
1919

@@ -23,10 +23,11 @@ This project hosts each sample app in separate repository branches. For more inf
2323
| Sample | Description |
2424
| ------------- | ------------- |
2525
| [main](https://github.com/googlesamples/android-architecture/tree/main) | This branch |
26-
| [hilt](https://github.com/googlesamples/android-architecture/tree/hilt) | A simple Hilt setup that removes the two flavors and service locator (not using Compose yet) |
26+
| [service-locator](https://github.com/googlesamples/android-architecture/tree/service-locator) | A simple setup that removes Hilt in favor of a service locator |
2727
| [livedata](https://github.com/googlesamples/android-architecture/tree/livedata) | Uses LiveData instead of StateFlow as the data stream solution |
2828
| [usecases](https://github.com/googlesamples/android-architecture/tree/usecases) | Adds a new domain layer that uses UseCases for business logic (not using Compose yet) |
2929
| [views](https://github.com/googlesamples/android-architecture/tree/views) | Uses Views instead of Jetpack Compose to render UI elements on the screen |
30+
| [views-hilt](https://github.com/googlesamples/android-architecture/tree/views-hilt) | Uses Views and Hilt instead together |
3031

3132
## Why a to-do app?
3233

app/build.gradle

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
apply plugin: 'com.android.application'
22
apply plugin: 'kotlin-android'
33
apply plugin: 'kotlin-kapt'
4+
apply plugin: 'dagger.hilt.android.plugin'
45

56
android {
67
compileSdkVersion rootProject.compileSdkVersion
@@ -12,7 +13,13 @@ android {
1213
versionCode 1
1314
versionName "1.0"
1415

15-
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
16+
testInstrumentationRunner "com.example.android.architecture.blueprints.todoapp.CustomTestRunner"
17+
18+
javaCompileOptions {
19+
annotationProcessorOptions {
20+
arguments += ["room.incremental" : "true"]
21+
}
22+
}
1623
}
1724

1825
android {
@@ -44,27 +51,6 @@ android {
4451
}
4552
}
4653

47-
flavorDimensions "default"
48-
49-
// If you need to add more flavors, consider using flavor dimensions.
50-
productFlavors {
51-
mock {
52-
dimension "default"
53-
applicationIdSuffix = ".mock"
54-
}
55-
prod {
56-
dimension "default"
57-
}
58-
}
59-
60-
// Remove mockRelease as it's not needed.
61-
android.variantFilter { variant ->
62-
if (variant.buildType.name == 'release'
63-
&& variant.getFlavors().get(0).name == 'mock') {
64-
variant.setIgnore(true)
65-
}
66-
}
67-
6854
// Always show the result of every unit test, even if it passes.
6955
testOptions.unitTests {
7056
includeAndroidResources = true
@@ -149,6 +135,11 @@ dependencies {
149135
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$archLifecycleVersion"
150136
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$archLifecycleVersion"
151137

138+
// Hilt
139+
implementation "com.google.dagger:hilt-android:$hiltVersion"
140+
implementation "androidx.hilt:hilt-navigation-compose:$hiltAndroidXVersion"
141+
kapt "com.google.dagger:hilt-compiler:$hiltVersion"
142+
152143
// Jetpack Compose
153144
implementation "androidx.activity:activity-compose:$activityComposeVersion"
154145
implementation "androidx.compose.material:material:$composeVersion"
@@ -175,6 +166,10 @@ dependencies {
175166
testImplementation "com.google.truth:truth:$truthVersion"
176167
testImplementation "androidx.compose.ui:ui-test-junit4:$composeVersion"
177168

169+
// JVM tests - Hilt
170+
testImplementation "com.google.dagger:hilt-android-testing:$hiltVersion"
171+
kaptTest "com.google.dagger:hilt-compiler:$hiltVersion"
172+
178173
// Dependencies for Android unit tests
179174
androidTestImplementation "junit:junit:$junitVersion"
180175
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion"
@@ -200,6 +195,10 @@ dependencies {
200195
androidTestImplementation "org.robolectric:annotations:$robolectricVersion"
201196
implementation "androidx.test.espresso:espresso-idling-resource:$espressoVersion"
202197

198+
// AndroidX Test - Hilt testing
199+
androidTestImplementation "com.google.dagger:hilt-android-testing:$hiltVersion"
200+
kaptAndroidTest "com.google.dagger:hilt-compiler:$hiltVersion"
201+
203202
// Kotlin
204203
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
205204
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (C) 2022 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.android.architecture.blueprints.todoapp
18+
19+
import android.app.Application
20+
import android.content.Context
21+
import androidx.test.runner.AndroidJUnitRunner
22+
import dagger.hilt.android.testing.HiltTestApplication
23+
24+
class CustomTestRunner : AndroidJUnitRunner() {
25+
override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
26+
return super.newApplication(cl, HiltTestApplication::class.java.name, context)
27+
}
28+
}
Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616
package com.example.android.architecture.blueprints.todoapp.tasks
1717

18-
import androidx.activity.ComponentActivity
1918
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
2019
import androidx.compose.ui.test.assertIsDisplayed
2120
import androidx.compose.ui.test.assertIsNotDisplayed
@@ -24,59 +23,50 @@ import androidx.compose.ui.test.onAllNodesWithText
2423
import androidx.compose.ui.test.onNodeWithContentDescription
2524
import androidx.compose.ui.test.onNodeWithText
2625
import androidx.compose.ui.test.performClick
27-
import androidx.test.core.app.ApplicationProvider.getApplicationContext
2826
import androidx.test.espresso.Espresso.pressBack
2927
import androidx.test.ext.junit.runners.AndroidJUnit4
3028
import androidx.test.filters.LargeTest
31-
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
29+
import com.example.android.architecture.blueprints.todoapp.HiltTestActivity
3230
import com.example.android.architecture.blueprints.todoapp.R
33-
import com.example.android.architecture.blueprints.todoapp.ServiceLocator
3431
import com.example.android.architecture.blueprints.todoapp.TodoNavGraph
3532
import com.example.android.architecture.blueprints.todoapp.data.Task
3633
import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository
3734
import com.example.android.architecture.blueprints.todoapp.util.saveTaskBlocking
3835
import com.google.accompanist.appcompattheme.AppCompatTheme
39-
import kotlinx.coroutines.Dispatchers
40-
import org.junit.After
36+
import dagger.hilt.android.testing.HiltAndroidRule
37+
import dagger.hilt.android.testing.HiltAndroidTest
4138
import org.junit.Assert.assertTrue
4239
import org.junit.Before
4340
import org.junit.Rule
4441
import org.junit.Test
4542
import org.junit.runner.RunWith
43+
import javax.inject.Inject
4644

4745
/**
4846
* Tests for scenarios that requires navigating within the app.
4947
*/
5048
@RunWith(AndroidJUnit4::class)
5149
@LargeTest
50+
@HiltAndroidTest
5251
class AppNavigationTest {
5352

54-
private lateinit var tasksRepository: TasksRepository
55-
56-
@get:Rule
57-
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
58-
private val activity get() = composeTestRule.activity
53+
@get:Rule(order = 0)
54+
var hiltRule = HiltAndroidRule(this)
5955

6056
// Executes tasks in the Architecture Components in the same thread
61-
@get:Rule
57+
@get:Rule(order = 1)
6258
var instantTaskExecutorRule = InstantTaskExecutorRule()
6359

60+
@get:Rule(order = 2)
61+
val composeTestRule = createAndroidComposeRule<HiltTestActivity>()
62+
private val activity get() = composeTestRule.activity
63+
64+
@Inject
65+
lateinit var tasksRepository: TasksRepository
66+
6467
@Before
6568
fun init() {
66-
// Run on UI thread to make sure the same instance of the SL is used.
67-
runOnUiThread {
68-
ServiceLocator.createDataBase(getApplicationContext(), inMemory = true)
69-
ServiceLocator.ioDispatcher = Dispatchers.Unconfined
70-
tasksRepository = ServiceLocator.provideTasksRepository(getApplicationContext())
71-
}
72-
}
73-
74-
@After
75-
fun reset() {
76-
runOnUiThread {
77-
ServiceLocator.resetRepository()
78-
ServiceLocator.resetIODispatcher()
79-
}
69+
hiltRule.inject()
8070
}
8171

8272
@Test
Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package com.example.android.architecture.blueprints.todoapp.tasks
1818

19-
import androidx.activity.ComponentActivity
2019
import androidx.annotation.StringRes
2120
import androidx.compose.material.Surface
2221
import androidx.compose.ui.test.assertIsDisplayed
@@ -28,20 +27,19 @@ import androidx.compose.ui.test.performClick
2827
import androidx.lifecycle.SavedStateHandle
2928
import androidx.test.ext.junit.runners.AndroidJUnit4
3029
import androidx.test.filters.MediumTest
31-
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
30+
import com.example.android.architecture.blueprints.todoapp.HiltTestActivity
3231
import com.example.android.architecture.blueprints.todoapp.R
33-
import com.example.android.architecture.blueprints.todoapp.ServiceLocator
3432
import com.example.android.architecture.blueprints.todoapp.data.Task
35-
import com.example.android.architecture.blueprints.todoapp.data.source.FakeRepository
3633
import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository
3734
import com.example.android.architecture.blueprints.todoapp.util.saveTaskBlocking
3835
import com.google.accompanist.appcompattheme.AppCompatTheme
39-
import kotlinx.coroutines.ExperimentalCoroutinesApi
40-
import org.junit.After
36+
import dagger.hilt.android.testing.HiltAndroidRule
37+
import dagger.hilt.android.testing.HiltAndroidTest
4138
import org.junit.Before
4239
import org.junit.Rule
4340
import org.junit.Test
4441
import org.junit.runner.RunWith
42+
import javax.inject.Inject
4543

4644
/**
4745
* Integration test for the Task List screen.
@@ -51,29 +49,22 @@ import org.junit.runner.RunWith
5149
@MediumTest
5250
// @LooperMode(LooperMode.Mode.PAUSED)
5351
// @TextLayoutMode(TextLayoutMode.Mode.REALISTIC)
54-
@ExperimentalCoroutinesApi
52+
@HiltAndroidTest
5553
class TasksScreenTest {
5654

57-
private lateinit var repository: TasksRepository
55+
@get:Rule(order = 0)
56+
var hiltRule = HiltAndroidRule(this)
5857

59-
@get:Rule
60-
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
58+
@get:Rule(order = 1)
59+
val composeTestRule = createAndroidComposeRule<HiltTestActivity>()
6160
private val activity get() = composeTestRule.activity
6261

63-
@Before
64-
fun initRepository() {
65-
// Run on UI thread to make sure the same instance of the SL is used.
66-
runOnUiThread {
67-
repository = FakeRepository()
68-
ServiceLocator.tasksRepository = repository
69-
}
70-
}
62+
@Inject
63+
lateinit var repository: TasksRepository
7164

72-
@After
73-
fun cleanupDb() {
74-
runOnUiThread {
75-
ServiceLocator.resetRepository()
76-
}
65+
@Before
66+
fun init() {
67+
hiltRule.inject()
7768
}
7869

7970
@Test

0 commit comments

Comments
 (0)