Hilt test kılavuzu

Hilt gibi bağımlılık ekleme çerçevelerinin avantajlarından biri, kodunuzu test etmeyi kolaylaştırmasıdır.

Birim testleri

Hilt, birim testleri için gerekli değildir. Çünkü yapıcı enjeksiyonu kullanan bir sınıfı test ederken sınıfı örneklemek için Hilt'i kullanmanız gerekmez. Bunun yerine, yapıcı ek açıklamalı olmasa da yapıcıyı doğrudan çağırabilirsiniz. Bunun için sahte veya taklit bağımlılıkları iletmeniz yeterlidir:

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

Uçtan uca testler

Hilt, entegrasyon testleri için bağımlılıklarını üretim kodunuzda olduğu gibi enjekte eder. Hilt her test için otomatik olarak yeni bir bileşen grubu oluşturduğundan Hilt ile test yapmak için bakım gerekmez.

Test bağımlılıkları ekleme

Hilt'i testlerinizde kullanmak için projenize hilt-android-testing bağımlılığını ekleyin:

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

Kullanıcı arayüzü testi kurulumu

Hilt kullanan tüm kullanıcı arayüzü testlerine @HiltAndroidTest ile not eklemeniz gerekir. Bu ek açıklama, her test için Hilt bileşenlerini oluşturmaktan sorumludur.

Ayrıca, HiltAndroidRule öğesini test sınıfına eklemeniz gerekir. Bileşenlerin durumunu yönetir ve testinize ekleme yapmak için kullanılır:

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

Ardından, testinizin Hilt'in sizin için otomatik olarak oluşturduğu Application sınıfını bilmesi gerekir.

Test uygulaması

Hilt'i destekleyen bir Application nesnesinde Hilt kullanan enstrümante testler çalıştırmanız gerekir. Kitaplık, testlerde kullanılmak üzere HiltTestApplication sağlar. Testleriniz için farklı bir temel uygulama gerekiyorsa Testler için özel uygulama bölümüne bakın.

Test uygulamanızı araçlarla testlerinizde veya Robolectric testlerinizde çalışacak şekilde ayarlamanız gerekir. Aşağıdaki talimatlar Hilt'e özel değildir ancak testlerde çalıştırılacak özel bir uygulamanın nasıl belirtileceğine dair genel yönergelerdir.

Araçla test edilen testlerde test uygulamasını ayarlama

Hilt test uygulamasını araçla test edilen testlerde kullanmak için yeni bir test çalıştırıcıyı yapılandırmanız gerekir. Bu sayede Hilt, projenizdeki tüm enstrümante testler için çalışır. Aşağıdaki adımları uygulayın:

  1. androidTest klasöründe AndroidJUnitRunner'u genişleten özel bir sınıf oluşturun.
  2. newApplication işlevini geçersiz kılın ve oluşturulan Hilt test uygulamasının adını iletin.

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

Ardından, Gradle dosyanızda bu test çalıştırıcısını donanımla desteklenmiş birim testi kılavuzunda açıklandığı şekilde yapılandırın. Tam sınıf yolunu kullandığınızdan emin olun:

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"
    }
}
Robolectric testlerinde test uygulamasını ayarlama

Kullanıcı arayüzü katmanınızı test etmek için Robolectric kullanıyorsanız robolectric.properties dosyasında hangi uygulamanın kullanılacağını belirtebilirsiniz:

application = dagger.hilt.android.testing.HiltTestApplication

Alternatif olarak, Robolectric'in @Config ek açıklamasını kullanarak her testte uygulamayı ayrı ayrı yapılandırabilirsiniz:

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

4.2'den eski bir Android Gradle Plugin sürümü kullanıyorsanız modülünüzün build.gradle dosyasına aşağıdaki yapılandırmayı uygulayarak yerel birim testlerinde @AndroidEntryPoint sınıflarının dönüştürülmesini etkinleştirin:

Groovy

hilt {
    enableTransformForLocalTests = true
}

Kotlin

hilt {
    enableTransformForLocalTests = true
}

enableTransformForLocalTests hakkında daha fazla bilgiyi Hilt belgelerinde bulabilirsiniz.

Test özellikleri

Hilt, testlerinizde kullanıma hazır olduğunda test sürecini özelleştirmek için çeşitli özellikleri kullanabilirsiniz.

Testlere tür ekleme

Bir teste tür eklemek için alan ekleme için @Inject kullanın. Hilt'e @Inject alanlarını doldurmasını söylemek için hiltRule.inject() işlevini çağırın.

Aşağıdaki ölçülmüş test örneğine bakın:

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

Bağlantıyı değiştirme

Bir bağımlılığın sahte veya taklit örneğini eklemeniz gerekiyorsa Hilt'e, üretim kodunda kullandığı bağlamayı kullanmamasını ve bunun yerine farklı bir bağlama kullanmasını söylemeniz gerekir. Bir bağlamayı değiştirmek için, bağlamayı içeren modülü testte kullanmak istediğiniz bağlamaları içeren bir test modülüyle değiştirmeniz gerekir.

Örneğin, üretim kodunuzun AnalyticsService için aşağıdaki gibi bir bağlama beyanı olduğunu varsayalım:

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

Testlerdeki AnalyticsService bağlamasını değiştirmek için test veya androidTest klasöründe sahte bağımlılığı olan yeni bir Hilt modülü oluşturun ve @TestInstallIn ile ek açıklama ekleyin. Bu klasördeki tüm testlere bunun yerine sahte bağımlılık eklenir.

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

Tek bir testte bağlamayı değiştirme

Bir bağlamayı tüm testler yerine tek bir testte değiştirmek için @UninstallModules ek açıklamasını kullanarak Hilt modülünü testten kaldırın ve testin içinde yeni bir test modülü oluşturun.

Önceki sürümdeki AnalyticsService örneğini takip ederek, test sınıfında @UninstallModules ek açıklamasını kullanarak Hilt'e üretim modülünü yoksayacak şekilde başlayın:

Kotlin

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

Java

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

Ardından, cilt kapağını değiştirmeniz gerekir. Test sınıfında test bağlamasını tanımlayan yeni bir modül oluşturun:

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

Bu işlem yalnızca tek bir test sınıfının bağlamasını değiştirir. Tüm test sınıflarının bağlamasını değiştirmek istiyorsanız yukarıdaki bölümdeki @TestInstallIn ek açıklamasını kullanın. Alternatif olarak, test bağlamasını Robolectric testleri için test modülüne veya enstrümante testler için androidTest modülüne yerleştirebilirsiniz. Mümkün olduğunda @TestInstallIn kullanmanızı öneririz.

Yeni değerleri bağlama

Testinizdeki alanları Hilt bağımlılık grafiğine kolayca bağlamak için @BindValue ek açıklamasını kullanın. Bir alana @BindValue ile ek açıklama eklediğinizde, bu alan, belirtilen alan türünün altına, o alanda bulunan tüm niteliklerle birlikte bağlanır.

AnalyticsService örneğinde, @BindValue kullanarak AnalyticsService değerini sahte bir değerle değiştirebilirsiniz:

Kotlin

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

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

  ...
}

Java

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

  @BindValue AnalyticsService analyticsService = FakeAnalyticsService();

  ...
}

Bu, her ikisini de aynı anda yapmanıza olanak tanıyarak hem testinizde bir bağlamayı değiştirmeyi hem de bağlamaya referans vermeyi kolaylaştırır.

@BindValue, nitelikler ve diğer test ek açıklamalarıyla çalışır. Örneğin, Mockito gibi test kitaplıklarını kullanıyorsanız bunları Robolectric testinde aşağıdaki gibi kullanabilirsiniz:

Kotlin

...
class SettingsActivityTest {
  ...

  @BindValue @ExampleQualifier @Mock
  lateinit var qualifiedVariable: ExampleCustomType

  // Robolectric tests here
}

Java

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

  // Robolectric tests here
}

Çoklu bağlama eklemeniz gerekiyorsa @BindValue yerine @BindValueIntoSet ve @BindValueIntoMap ek açıklamalarını kullanabilirsiniz. @BindValueIntoMap, alana bir harita anahtarı ek açıklaması da eklemenizi gerektirir.

Özel durumlar

Hilt, standart olmayan kullanım alanlarını destekleyen özellikler de sunar.

Testler için özel uygulama

Test uygulamanızın başka bir uygulamayı genişletmesi gerektiği için HiltTestApplication kullanamıyorsanız oluşturulan Hilt uygulamasının genişletmesini istediğiniz temel sınıfın değerini ileterek yeni bir sınıfı veya arayüzü @CustomTestApplication ile ek açıklamayla belirtin.

@CustomTestApplication, Hilt ile parametre olarak ilettiğiniz uygulamayı genişleten ve teste hazır bir Application sınıfı oluşturur.

Kotlin

@CustomTestApplication(BaseApplication::class)
interface HiltTestApplication

Java

@CustomTestApplication(BaseApplication.class)
interface HiltTestApplication { }

Örnekte Hilt, BaseApplication sınıfını genişleten HiltTestApplication_Application adlı bir Application oluşturur. Genel olarak, oluşturulan uygulamanın adı, _Application eklenmiş ek açıklamalı sınıfın adıdır. Oluşturulan Hilt test uygulamasını, Test uygulaması bölümünde açıklandığı gibi araçlarla testlerinizde veya Robolectric testlerinizde çalıştıracak şekilde ayarlamanız gerekir.

Araçla test edilen testinizde birden fazla TestRule nesnesi

Testinizde başka TestRule nesneleriniz varsa tüm kuralların birlikte çalıştığından emin olmanın birden fazla yolu vardır.

Kuralları aşağıdaki gibi bir araya getirebilirsiniz:

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

Alternatif olarak, HiltAndroidRule önce yürütüldüğü sürece her iki kuralı da aynı düzeyde kullanabilirsiniz. @Rule ek açıklamalarında order özelliğini kullanarak yürütme sırasını belirtin. Bu yöntem yalnızca JUnit 4.13 veya sonraki sürümlerde çalışır:

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

Hilt, @AndroidEntryPoint ile ek açıklama eklenmemiş bir etkinliğe dayandığı için androidx.fragment:fragment-testing kitaplığındaki launchFragmentInContainer öğesini Hilt ile kullanmak mümkün değildir.

Bunun yerine architecture-samples GitHub deposundaki launchFragmentInHiltContainer kodunu kullanın.

Tekil bileşen kullanıma sunulmadan önce giriş noktası kullanma

@EarlyEntryPoint ek açıklama, Hilt testinde tekil bileşen kullanılabilir hale gelmeden önce Hilt giriş noktası oluşturulması gerektiğinde bir kaçış yolu sağlar.

@EarlyEntryPoint hakkında daha fazla bilgi için Hilt belgelerine göz atın.