Salah satu manfaat menggunakan framework injeksi dependensi seperti Hilt adalah memudahkan Anda menguji kode.
Pengujian Unit
Hilt tidak diperlukan untuk pengujian unit karena saat menguji class yang menggunakan injeksi konstruktor, Anda tidak perlu menggunakan Hilt untuk membuat instance class tersebut. Sebaliknya, Anda dapat langsung memanggil konstruktor class dengan meneruskan dependensi tiruan atau palsu, sama seperti yang akan Anda lakukan jika konstruktor tidak dianotasi:
@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(...) } }
Hal yang sama berlaku untuk class ViewModel yang diperoleh dengan memanggil hiltViewModel() di
composable Anda. Dalam pengujian unit, buat ViewModel secara langsung dengan tiruan.
Untuk mengetahui informasi tentang cara status mengalir dari ViewModel ke composable, lihat
Status dan Jetpack Compose serta Tempat mengangkat status.
Pengujian menyeluruh
Untuk pengujian integrasi, Hilt menginjeksikan dependensi seperti yang terjadi pada kode produksi Anda. Pengujian dengan Hilt tidak memerlukan pemeliharaan karena Hilt otomatis menghasilkan serangkaian komponen baru untuk setiap pengujian.
Menambahkan dependensi pengujian
Untuk menggunakan Hilt dalam pengujian, sertakan dependensi hilt-android-testing dalam project
Anda:
dependencies { // For Robolectric tests. testImplementation("com.google.dagger:hilt-android-testing:2.57.1") kspTest("com.google.dagger:hilt-android-compiler:2.57.1") // For instrumented tests. androidTestImplementation("com.google.dagger:hilt-android-testing:2.57.1") kspAndroidTest("com.google.dagger:hilt-android-compiler:2.57.1") // Compose UI test rule. androidTestImplementation("androidx.compose.ui:ui-test-junit4") debugImplementation("androidx.compose.ui:ui-test-manifest") }
Penyiapan pengujian UI
Anda harus menganotasi setiap pengujian UI yang menggunakan Hilt dengan @HiltAndroidTest. Anotasi
ini bertanggung jawab untuk menghasilkan komponen Hilt untuk setiap pengujian.
Selain itu, Anda perlu menambahkan HiltAndroidRule ke class pengujian. Fitur ini mengelola
status komponen dan digunakan untuk melakukan injeksi pada pengujian Anda:
@HiltAndroidTest class SettingsScreenTest { @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this) @get:Rule(order = 1) val composeRule = createAndroidComposeRule<HiltTestActivity>() // Compose UI tests here. }
Selanjutnya, pengujian Anda perlu mengetahui class Application yang otomatis dihasilkan
Hilt untuk Anda.
Agar Hilt dapat menyuntikkan dependensi, Anda harus membuat aktivitas kosong bernama
HiltTestActivity di set sumber androidTest dan menganotasinya dengan
@AndroidEntryPoint. createAndroidComposeRule kemudian menggunakan aktivitas ini sebagai
host untuk konten composable Anda.
Aplikasi pengujian
Anda harus menjalankan uji instrumentasi yang menggunakan Hilt pada objek Application
yang mendukung Hilt. Library ini menyediakan HiltTestApplication untuk digunakan dalam pengujian.
Jika pengujian memerlukan aplikasi dasar berbeda, lihat Aplikasi kustom untuk
pengujian.
Anda harus menetapkan aplikasi pengujian untuk dijalankan dalam uji instrumentasi atau uji Robolectric. Petunjuk berikut tidak khusus untuk Hilt, tetapi merupakan panduan umum cara menentukan aplikasi kustom yang akan dijalankan dalam pengujian.
Menetapkan aplikasi pengujian dalam uji instrumentasi
Untuk menggunakan aplikasi pengujian Hilt dalam uji instrumentasi, Anda perlu mengonfigurasi runner pengujian baru. Hal ini membuat Hilt berfungsi untuk semua uji instrumentasi dalam project Anda. Lakukan langkah-langkah berikut:
- Buat class kustom yang memperluas
AndroidJUnitRunnerdi folderandroidTest. - Ganti fungsi
newApplicationdan teruskan dengan nama aplikasi pengujian Hilt yang dihasilkan.
// 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) } }
Selanjutnya, konfigurasikan runner pengujian ini dalam file Gradle Anda seperti yang dijelaskan dalam panduan pengujian unit instrumentasi. Pastikan Anda menggunakan classpath penuh:
android { defaultConfig { // Replace com.example.android.dagger with your class path. testInstrumentationRunner = "com.example.android.dagger.CustomTestRunner" } }
Menetapkan aplikasi pengujian dalam uji Robolectric
Jika Anda menggunakan Robolectric untuk menguji lapisan UI, Anda dapat menentukan aplikasi
mana yang akan digunakan dalam file robolectric.properties:
application = dagger.hilt.android.testing.HiltTestApplication
Atau, Anda dapat mengonfigurasi aplikasi satu per satu pada setiap pengujian menggunakan
anotasi @Config Robolectric:
@HiltAndroidTest @Config(application = HiltTestApplication::class) class SettingsScreenTest { @get:Rule var hiltRule = HiltAndroidRule(this) // Robolectric tests here. }
Fitur Pengujian
Setelah Hilt siap digunakan dalam pengujian, Anda dapat menggunakan beberapa fitur untuk menyesuaikan proses pengujian.
Menginjeksikan jenis dalam pengujian
Untuk menginjeksikan jenis ke dalam pengujian, gunakan @Inject untuk injeksi kolom. Untuk memberi tahu Hilt agar
mengisi kolom @Inject, panggil hiltRule.inject().
Lihat contoh uji instrumentasi berikut:
@HiltAndroidTest class SettingsScreenTest { @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this) @get:Rule(order = 1) val composeRule = createAndroidComposeRule<HiltTestActivity>() @Inject lateinit var analyticsAdapter: AnalyticsAdapter @Before fun init() { hiltRule.inject() } @Test fun settingsScreen_showsTitle() { composeRule.setContent { SettingsScreen() } composeRule.onNodeWithText("Settings").assertIsDisplayed() // analyticsRepository is available here. } }
Mengganti binding
Jika Anda perlu menginjeksikan instance tiruan atau palsu dari dependensi, Anda perlu memberi tahu Hilt agar tidak menggunakan binding dalam kode produksi dan menggunakan yang lain. Untuk mengganti binding, Anda harus mengganti modul yang berisi binding, dengan modul pengujian berisi binding yang ingin digunakan dalam pengujian.
Misalnya, anggap kode produksi Anda mendeklarasikan binding untuk
AnalyticsService sebagai berikut:
@Module @InstallIn(SingletonComponent::class) abstract class AnalyticsModule { @Singleton @Binds abstract fun bindAnalyticsService( analyticsServiceImpl: AnalyticsServiceImpl ): AnalyticsService }
Untuk mengganti binding AnalyticsService dalam pengujian, buat modul Hilt baru di
folder test atau androidTest dengan dependensi palsu dan anotasikan
dengan @TestInstallIn. Sebagai gantinya, semua pengujian di folder tersebut dimasukkan dengan dependensi
palsu.
@Module @TestInstallIn( components = [SingletonComponent::class], replaces = [AnalyticsModule::class] ) abstract class FakeAnalyticsModule { @Singleton @Binds abstract fun bindAnalyticsService( fakeAnalyticsService: FakeAnalyticsService ): AnalyticsService }
Karena composable biasanya menggunakan dependensi ini secara tidak langsung melalui
ViewModel yang diperoleh dengan hiltViewModel(), mengganti binding di Hilt sudah
cukup. Composable yang sedang diuji akan mengambil tiruan secara otomatis.
Mengganti binding di satu pengujian
Untuk mengganti binding di satu pengujian, bukan semua pengujian, uninstal modul
Hilt dari pengujian menggunakan anotasi @UninstallModules dan buat
modul pengujian baru di dalam pengujian.
Dengan mengikuti contoh AnalyticsService dari versi sebelumnya, mulailah dengan
memberi tahu Hilt untuk mengabaikan modul produksi menggunakan anotasi @UninstallModules
di class pengujian:
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsScreenTest { ... }
Selanjutnya, Anda harus mengganti binding. Buat modul baru dalam class pengujian yang menentukan binding pengujian:
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsScreenTest { @Module @InstallIn(SingletonComponent::class) abstract class TestModule { @Singleton @Binds abstract fun bindAnalyticsService( fakeAnalyticsService: FakeAnalyticsService ): AnalyticsService } // ... }
Ini hanya menggantikan binding untuk satu class pengujian. Jika Anda ingin mengganti
binding untuk semua class pengujian, gunakan anotasi @TestInstallIn dari
bagian di atas. Atau, Anda dapat memasukkan binding pengujian dalam modul test
untuk pengujian Robolectric, atau dalam modul androidTest untuk uji instrumentasi.
Sebaiknya gunakan @TestInstallIn jika memungkinkan.
Mengikatkan nilai baru
Gunakan anotasi @BindValue untuk mengikatkan kolom dalam pengujian Anda ke grafik
dependensi Hilt dengan mudah. Anotasi kolom dengan @BindValue dan kolomnya akan terikat dalam
jenis kolom yang dideklarasikan dengan penentu apa pun yang ada untuk kolom tersebut.
Dalam contoh AnalyticsService, Anda dapat mengganti AnalyticsService dengan
yang palsu menggunakan @BindValue:
@UninstallModules(AnalyticsModule::class) @HiltAndroidTest class SettingsScreenTest { @BindValue @JvmField val analyticsService: AnalyticsService = FakeAnalyticsService() ... }
Hal ini menyederhanakan penggantian binding dan perujukan binding dalam pengujian dengan memungkinkan Anda melakukan keduanya secara bersamaan.
@BindValue berfungsi dengan penentu dan anotasi pengujian lainnya. Misalnya,
jika Anda menggunakan library pengujian seperti Mockito, Anda dapat menggunakannya dalam
uji Robolectric seperti berikut:
... class SettingsScreenTest { ... @BindValue @ExampleQualifier @Mock lateinit var qualifiedVariable: ExampleCustomType // Robolectric tests here }
Jika perlu menambahkan multibinding,
Anda dapat menggunakan anotasi @BindValueIntoSet dan @BindValueIntoMap sebagai ganti
@BindValue. @BindValueIntoMap mewajibkan Anda untuk menganotasi juga kolom
dengan anotasi map key.
Kasus tertentu
Hilt juga menyediakan fitur untuk mendukung kasus penggunaan non-standar.
Aplikasi kustom untuk pengujian
Jika Anda tidak dapat menggunakan HiltTestApplication karena aplikasi pengujian Anda perlu memperluas aplikasi lain, anotasilah class atau antarmuka baru dengan @CustomTestApplication, sehingga meneruskan nilai class dasar yang Anda inginkan untuk diperluas oleh aplikasi Hilt yang dihasilkan.
@CustomTestApplication akan menghasilkan class Application yang siap diuji
dengan Hilt yang memperluas aplikasi yang Anda teruskan sebagai parameter.
@CustomTestApplication(BaseApplication::class) interface HiltTestApplication
Dalam contoh ini, Hilt menghasilkan Application bernama
HiltTestApplication_Application yang memperluas class BaseApplication. Secara umum, nama aplikasi yang dihasilkan adalah nama class yang dianotasi dan ditambahkan dengan _Application. Anda harus menetapkan aplikasi
pengujian Hilt yang dihasilkan untuk dijalankan dalam uji instrumentasi atau uji Robolectric seperti yang dijelaskan dalam Aplikasi
pengujian.
Beberapa objek TestRule dalam uji instrumentasi
Pengujian UI Compose sudah menggabungkan HiltAndroidRule dengan aturan pengujian Compose
seperti createAndroidComposeRule. Jika Anda memiliki objek TestRule tambahan, pastikan HiltAndroidRule berjalan terlebih dahulu. Nyatakan urutan eksekusi dengan atribut
order pada @Rule:
@HiltAndroidTest class SettingsScreenTest { @get:Rule(order = 0) var hiltRule = HiltAndroidRule(this) @get:Rule(order = 1) val composeRule = createAndroidComposeRule<HiltTestActivity>() @get:Rule(order = 2) val otherRule = SomeOtherRule() // UI tests here. }
Atau, Anda dapat menggabungkan aturan dengan RuleChain, menempatkan
HiltAndroidRule sebagai aturan terluar.
@HiltAndroidTest class SettingsScreenTest { @get:Rule var rule = RuleChain.outerRule(HiltAndroidRule(this)). around(SettingsScreenTestRule(...)) // UI tests here. }
Menggunakan titik entri sebelum komponen singleton tersedia
Anotasi @EarlyEntryPoint menyediakan jalan keluar saat titik masuk
Hilt harus dibuat sebelum komponen singleton tersedia dalam
pengujian Hilt.
Informasi selengkapnya tentang @EarlyEntryPoint ada dalam
dokumentasi Hilt.
Referensi lainnya
Untuk mempelajari pengujian lebih lanjut, lihat referensi tambahan berikut: