Panduan pengujian Hilt

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:

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(...);
  }
}

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:

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")
}

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:

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.
}

Selanjutnya, pengujian Anda perlu mengetahui class Application yang otomatis dihasilkan Hilt untuk 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:

  1. Buat class kustom yang memperluas AndroidJUnitRunner di folder androidTest.
  2. Ganti fungsi newApplication dan teruskan dengan nama aplikasi pengujian Hilt yang dihasilkan.

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);
  }
}

Selanjutnya, konfigurasikan runner pengujian ini dalam file Gradle Anda seperti yang dijelaskan dalam panduan pengujian unit instrumentasi. Pastikan Anda menggunakan classpath penuh:

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"
    }
}
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:

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.
}

Jika Anda menggunakan Versi Plugin Android Gradle yang lebih rendah dari 4.2, aktifkan transformasi class @AndroidEntryPoint dalam pengujian unit lokal dengan menerapkan konfigurasi berikut dalam file build.gradle modul Anda:

Groovy

hilt {
    enableTransformForLocalTests = true
}

Kotlin

hilt {
    enableTransformForLocalTests = true
}

Informasi selengkapnya tentang enableTransformForLocalTests ada dalam dokumentasi Hilt.

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:

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.
  }
}

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:

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
  );
}

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.

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
  );
}

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:

Kotlin

@UninstallModules(AnalyticsModule::class)
@HiltAndroidTest
class SettingsActivityTest { ... }

Java

@UninstallModules(AnalyticsModule.class)
@HiltAndroidTest
public final class SettingsActivityTest { ... }

Selanjutnya, Anda harus mengganti binding. Buat modul baru dalam class pengujian yang menentukan binding pengujian:

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
    );
  }
  ...
}

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:

Kotlin

@UninstallModules(AnalyticsModule::class)
@HiltAndroidTest
class SettingsActivityTest {

  @BindValue @JvmField
  val analyticsService: AnalyticsService = FakeAnalyticsService()

  ...
}

Java

@UninstallModules(AnalyticsModule.class)
@HiltAndroidTest
class SettingsActivityTest {

  @BindValue 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:

Kotlin

...
class SettingsActivityTest {
  ...

  @BindValue @ExampleQualifier @Mock
  lateinit var qualifiedVariable: ExampleCustomType

  // Robolectric tests here
}

Java

...
class SettingsActivityTest {
  ...
  @BindValue @ExampleQualifier @Mock ExampleCustomType qualifiedVariable;

  // 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.

Kotlin

@CustomTestApplication(BaseApplication::class)
interface HiltTestApplication

Java

@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

Jika Anda memiliki objek TestRule lain dalam pengujian, ada beberapa cara untuk memastikan bahwa semua aturan berfungsi bersama.

Anda dapat menggabungkan aturan sebagai berikut:

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.
}

Atau, Anda dapat menggunakan kedua aturan di tingkat yang sama selama HiltAndroidRule dijalankan terlebih dahulu. Tentukan urutan eksekusi menggunakan atribut order dalam anotasi @Rule. Ini hanya berfungsi di JUnit versi 4.13 atau yang lebih tinggi:

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

Penggunaan launchFragmentInContainer dari library androidx.fragment:fragment-testing dengan Hilt tidak dapat dilakukan karena mengandalkan aktivitas yang tidak dianotasi dengan @AndroidEntryPoint.

Gunakan kode launchFragmentInHiltContainer dari repositori GitHub architecture-samples.

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.