Руководство по тестированию рукояти,Руководство по тестированию рукояти

Одним из преимуществ использования фреймворков внедрения зависимостей, таких как Hilt, является то, что они упрощают тестирование вашего кода.

Модульные тесты

Hilt не требуется для модульных тестов, поскольку при тестировании класса, использующего внедрение конструктора, вам не нужно использовать Hilt для создания экземпляра этого класса. Вместо этого вы можете напрямую вызвать конструктор класса, передав поддельные или макетные зависимости, как если бы конструктор не был аннотирован:

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

Сквозные тесты

Для интеграционных тестов Hilt внедряет зависимости так же, как и в ваш производственный код. Тестирование с помощью Hilt не требует обслуживания, поскольку Hilt автоматически генерирует новый набор компонентов для каждого теста.

Добавление зависимостей тестирования

Чтобы использовать Hilt в своих тестах, включите в свой проект зависимость hilt-android-testing :

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

Настройка тестирования пользовательского интерфейса

Вы должны аннотировать любой тест пользовательского интерфейса, использующий Hilt, с помощью @HiltAndroidTest . Эта аннотация отвечает за создание компонентов Hilt для каждого теста.

Кроме того, вам необходимо добавить HiltAndroidRule в тестовый класс. Он управляет состоянием компонентов и используется для внедрения в ваш тест:

@HiltAndroidTest
class SettingsActivityTest {

  @get:Rule
  var hiltRule = HiltAndroidRule(this)

  // UI tests here.
}
@HiltAndroidTest
public final class SettingsActivityTest {

  @Rule
  public HiltAndroidRule hiltRule = new HiltAndroidRule(this);

  // UI tests here.
}

Далее, ваш тест должен знать о классе Application , который Hilt автоматически генерирует для вас.

Тестовое приложение

Вы должны выполнить инструментированные тесты, использующие Hilt, в объекте Application , поддерживающем Hilt. Библиотека предоставляет HiltTestApplication для использования в тестах. Если для ваших тестов требуется другое базовое приложение, см. раздел Пользовательское приложение для тестов .

Вы должны настроить тестовое приложение для работы в инструментальных тестах или тестах Robolectric . Следующие инструкции не относятся конкретно к Hilt, но представляют собой общие рекомендации по тому, как указать собственное приложение для запуска в тестах.

Установите тестовое приложение в инструментальных тестах.

Чтобы использовать тестовое приложение Hilt в инструментальных тестах , вам необходимо настроить новый инструмент запуска тестов. Благодаря этому Hilt будет работать для всех инструментальных тестов в вашем проекте. Выполните следующие шаги:

  1. Создайте собственный класс, расширяющий AndroidJUnitRunner в папке androidTest .
  2. Переопределите функцию newApplication и передайте имя созданного тестового приложения Hilt.
// 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)
    }
}
// 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);
  }
}

Затем настройте этот инструмент запуска тестов в файле Gradle, как описано в руководстве по инструментированному модульному тестированию . Убедитесь, что вы используете полный путь к классам:

android {
    defaultConfig {
        // Replace com.example.android.dagger with your class path.
        testInstrumentationRunner "com.example.android.dagger.CustomTestRunner"
    }
}
android {
    defaultConfig {
        // Replace com.example.android.dagger with your class path.
        testInstrumentationRunner = "com.example.android.dagger.CustomTestRunner"
    }
}
Установите тестовое приложение в тесты Robolectric.

Если вы используете Robolectric для тестирования уровня пользовательского интерфейса, вы можете указать, какое приложение использовать, в файле robolectric.properties :

application = dagger.hilt.android.testing.HiltTestApplication

Кроме того, вы можете настроить приложение для каждого теста индивидуально, используя аннотацию @Config Robolectric:

@HiltAndroidTest
@Config(application = HiltTestApplication::class)
class SettingsActivityTest {

  @get:Rule
  var hiltRule = HiltAndroidRule(this)

  // Robolectric tests here.
}
@HiltAndroidTest
@Config(application = HiltTestApplication.class)
class SettingsActivityTest {

  @Rule public HiltAndroidRule hiltRule = new HiltAndroidRule(this);

  // Robolectric tests here.
}

Если вы используете подключаемый модуль Android Gradle версии ниже 4.2, включите преобразование классов @AndroidEntryPoint в локальных модульных тестах, применив следующую конфигурацию в файле build.gradle вашего модуля:

hilt {
    enableTransformForLocalTests = true
}
hilt {
    enableTransformForLocalTests = true
}

Больше информации о enableTransformForLocalTests в документации Hilt .

Возможности тестирования

Как только Hilt будет готов к использованию в ваших тестах, вы сможете использовать несколько функций для настройки процесса тестирования.

Типы внедрения в тестах

Чтобы внедрить типы в тест, используйте @Inject для внедрения полей. Чтобы указать Hilt заполнить поля @Inject , вызовите hiltRule.inject() .

См. следующий пример инструментального теста:

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

Заменить привязку

Если вам нужно внедрить поддельный или имитационный экземпляр зависимости, вам нужно указать Hilt не использовать привязку, которую она использовала в рабочем коде, а вместо этого использовать другую. Чтобы заменить привязку, вам необходимо заменить модуль, содержащий привязку, на тестовый модуль, содержащий привязки, которые вы хотите использовать в тесте.

Например, предположим, что ваш производственный код объявляет привязку для AnalyticsService следующим образом:

@Module
@InstallIn(SingletonComponent::class)
abstract class AnalyticsModule {

  @Singleton
  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}
@Module
@InstallIn(SingletonComponent.class)
public abstract class AnalyticsModule {

  @Singleton
  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    AnalyticsServiceImpl analyticsServiceImpl
  );
}

Чтобы заменить привязку AnalyticsService в тестах, создайте новый модуль Hilt в папке test или androidTest с поддельной зависимостью и аннотируйте его @TestInstallIn . Вместо этого во все тесты в этой папке вводятся поддельные зависимости.

@Module
@TestInstallIn(
    components = [SingletonComponent::class],
    replaces = [AnalyticsModule::class]
)
abstract class FakeAnalyticsModule {

  @Singleton
  @Binds
  abstract fun bindAnalyticsService(
    fakeAnalyticsService: FakeAnalyticsService
  ): AnalyticsService
}
@Module
@TestInstallIn(
    components = SingletonComponent.class,
    replaces = AnalyticsModule.class
)
public abstract class FakeAnalyticsModule {

  @Singleton
  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    FakeAnalyticsService fakeAnalyticsService
  );
}

Замена привязки за один тест

Чтобы заменить привязку в одном тесте, а не во всех тестах, удалите модуль Hilt из теста с помощью аннотации @UninstallModules и создайте новый тестовый модуль внутри теста.

Следуя примеру AnalyticsService из предыдущей версии, начните с указания Hilt игнорировать рабочий модуль, используя аннотацию @UninstallModules в тестовом классе:

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

Далее необходимо заменить обвязку. Создайте новый модуль в тестовом классе, который определяет привязку теста:

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

  @Module
  @InstallIn(SingletonComponent::class)
  abstract class TestModule {

    @Singleton
    @Binds
    abstract fun bindAnalyticsService(
      fakeAnalyticsService: FakeAnalyticsService
    ): AnalyticsService
  }

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

  @Module
  @InstallIn(SingletonComponent.class)
  public abstract class TestModule {

    @Singleton
    @Binds
    public abstract AnalyticsService bindAnalyticsService(
      FakeAnalyticsService fakeAnalyticsService
    );
  }
  ...
}

Это заменяет привязку только для одного тестового класса. Если вы хотите заменить привязку для всех тестовых классов, используйте аннотацию @TestInstallIn из раздела выше. Альтернативно вы можете поместить привязку теста в test модуль для тестов Robolectric или в модуль androidTest для инструментальных тестов. Рекомендуется использовать @TestInstallIn когда это возможно.

Привязка новых ценностей

Используйте аннотацию @BindValue , чтобы легко связать поля вашего теста с графом зависимостей Hilt. Добавьте к полю аннотацию @BindValue , и оно будет привязано к объявленному типу поля с любыми квалификаторами, которые присутствуют для этого поля.

В примере AnalyticsService вы можете заменить AnalyticsService поддельным, используя @BindValue :

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

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

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

  @BindValue AnalyticsService analyticsService = FakeAnalyticsService();

  ...
}

Это упрощает как замену привязки, так и ссылку на привязку в тесте, позволяя делать и то, и другое одновременно.

@BindValue работает с квалификаторами и другими аннотациями тестирования. Например, если вы используете библиотеки тестирования, такие как Mockito , вы можете использовать их в тесте Robolectric следующим образом:

...
class SettingsActivityTest {
  ...

  @BindValue @ExampleQualifier @Mock
  lateinit var qualifiedVariable: ExampleCustomType

  // Robolectric tests here
}
...
class SettingsActivityTest {
  ...
  @BindValue @ExampleQualifier @Mock ExampleCustomType qualifiedVariable;

  // Robolectric tests here
}

Если вам нужно добавить мультипривязку , вы можете использовать аннотации @BindValueIntoSet и @BindValueIntoMap вместо @BindValue . @BindValueIntoMap требует, чтобы вы также аннотировали поле аннотацией ключа карты.

Особые случаи

Hilt также предоставляет функции для поддержки нестандартных случаев использования.

Пользовательское приложение для тестов

Если вы не можете использовать HiltTestApplication поскольку вашему тестовому приложению необходимо расширить другое приложение, аннотируйте новый класс или интерфейс с помощью @CustomTestApplication , передав значение базового класса, который вы хотите расширить сгенерированное приложение Hilt.

@CustomTestApplication сгенерирует класс Application , готовый к тестированию с помощью Hilt, который расширяет приложение, которое вы передали в качестве параметра.

@CustomTestApplication(BaseApplication::class)
interface HiltTestApplication
@CustomTestApplication(BaseApplication.class)
interface HiltTestApplication { }

В этом примере Hilt создает Application с именем HiltTestApplication_Application , которое расширяет класс BaseApplication . Обычно имя созданного приложения — это имя аннотированного класса, к которому добавляется _Application . Вы должны настроить созданное тестовое приложение Hilt для запуска в ваших инструментальных тестах или тестах Robolectric , как описано в разделе Тестовое приложение .

Несколько объектов TestRule в инструментированном тесте

Если в вашем тесте есть другие объекты TestRule , существует несколько способов убедиться, что все правила работают вместе.

Вы можете объединить правила следующим образом:

@HiltAndroidTest
class SettingsActivityTest {

  @get:Rule
  var rule = RuleChain.outerRule(HiltAndroidRule(this)).
        around(SettingsActivityTestRule(...))

  // UI tests here.
}
@HiltAndroidTest
public final class SettingsActivityTest {

  @Rule public RuleChain rule = RuleChain.outerRule(new HiltAndroidRule(this))
        .around(new SettingsActivityTestRule(...));

  // UI tests here.
}

Альтернативно вы можете использовать оба правила на одном уровне, если HiltAndroidRule выполняется первым. Укажите порядок выполнения, используя атрибут order в аннотации @Rule . Это работает только в JUnit версии 4.13 или выше:

@HiltAndroidTest
class SettingsActivityTest {

  @get:Rule(order = 0)
  var hiltRule = HiltAndroidRule(this)

  @get:Rule(order = 1)
  var settingsActivityTestRule = SettingsActivityTestRule(...)

  // UI tests here.
}
@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 из библиотеки androidx.fragment:fragment-testing с Hilt, поскольку она опирается на действие, не помеченное @AndroidEntryPoint .

Вместо этого используйте код launchFragmentInHiltContainer из репозитория architecture-samples GitHub.

Используйте точку входа до того, как будет доступен одноэлементный компонент.

Аннотация @EarlyEntryPoint предоставляет запасной выход, когда необходимо создать точку входа Hilt до того, как одноэлементный компонент станет доступен в тесте Hilt.

Дополнительная информация о @EarlyEntryPoint в документации Hilt .

,

Одним из преимуществ использования фреймворков внедрения зависимостей, таких как Hilt, является то, что они упрощают тестирование вашего кода.

Модульные тесты

Hilt не требуется для модульных тестов, поскольку при тестировании класса, использующего внедрение конструктора, вам не нужно использовать Hilt для создания экземпляра этого класса. Вместо этого вы можете напрямую вызвать конструктор класса, передав поддельные или макетные зависимости, как если бы конструктор не был аннотирован:

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

Сквозные тесты

Для интеграционных тестов Hilt внедряет зависимости так же, как и в ваш производственный код. Тестирование с помощью Hilt не требует обслуживания, поскольку Hilt автоматически генерирует новый набор компонентов для каждого теста.

Добавление зависимостей тестирования

Чтобы использовать Hilt в своих тестах, включите в свой проект зависимость hilt-android-testing :

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

Настройка тестирования пользовательского интерфейса

Вы должны аннотировать любой тест пользовательского интерфейса, использующий Hilt, с помощью @HiltAndroidTest . Эта аннотация отвечает за создание компонентов Hilt для каждого теста.

Кроме того, вам необходимо добавить HiltAndroidRule в тестовый класс. Он управляет состоянием компонентов и используется для внедрения в ваш тест:

@HiltAndroidTest
class SettingsActivityTest {

  @get:Rule
  var hiltRule = HiltAndroidRule(this)

  // UI tests here.
}
@HiltAndroidTest
public final class SettingsActivityTest {

  @Rule
  public HiltAndroidRule hiltRule = new HiltAndroidRule(this);

  // UI tests here.
}

Далее, ваш тест должен знать о классе Application , который Hilt автоматически генерирует для вас.

Тестовое приложение

Вы должны выполнить инструментированные тесты, использующие Hilt, в объекте Application , поддерживающем Hilt. Библиотека предоставляет HiltTestApplication для использования в тестах. Если для ваших тестов требуется другое базовое приложение, см. раздел Пользовательское приложение для тестов .

Вы должны настроить тестовое приложение для работы в инструментальных тестах или тестах Robolectric . Следующие инструкции не относятся конкретно к Hilt, но представляют собой общие рекомендации по тому, как указать собственное приложение для запуска в тестах.

Установите тестовое приложение в инструментированные тесты.

Чтобы использовать тестовое приложение Hilt в инструментальных тестах , вам необходимо настроить новый инструмент запуска тестов. Благодаря этому Hilt будет работать для всех инструментальных тестов в вашем проекте. Выполните следующие шаги:

  1. Создайте собственный класс, расширяющий AndroidJUnitRunner в папке androidTest .
  2. Переопределите функцию newApplication и передайте имя созданного тестового приложения Hilt.
// 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)
    }
}
// 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);
  }
}

Затем настройте этот инструмент запуска тестов в файле Gradle, как описано в руководстве по инструментированному модульному тестированию . Убедитесь, что вы используете полный путь к классам:

android {
    defaultConfig {
        // Replace com.example.android.dagger with your class path.
        testInstrumentationRunner "com.example.android.dagger.CustomTestRunner"
    }
}
android {
    defaultConfig {
        // Replace com.example.android.dagger with your class path.
        testInstrumentationRunner = "com.example.android.dagger.CustomTestRunner"
    }
}
Установите тестовое приложение в тесты Robolectric.

Если вы используете Robolectric для тестирования уровня пользовательского интерфейса, вы можете указать, какое приложение использовать, в файле robolectric.properties :

application = dagger.hilt.android.testing.HiltTestApplication

Кроме того, вы можете настроить приложение для каждого теста индивидуально, используя аннотацию @Config Robolectric:

@HiltAndroidTest
@Config(application = HiltTestApplication::class)
class SettingsActivityTest {

  @get:Rule
  var hiltRule = HiltAndroidRule(this)

  // Robolectric tests here.
}
@HiltAndroidTest
@Config(application = HiltTestApplication.class)
class SettingsActivityTest {

  @Rule public HiltAndroidRule hiltRule = new HiltAndroidRule(this);

  // Robolectric tests here.
}

Если вы используете подключаемый модуль Android Gradle версии ниже 4.2, включите преобразование классов @AndroidEntryPoint в локальных модульных тестах, применив следующую конфигурацию в файле build.gradle вашего модуля:

hilt {
    enableTransformForLocalTests = true
}
hilt {
    enableTransformForLocalTests = true
}

Больше информации о enableTransformForLocalTests в документации Hilt .

Возможности тестирования

Как только Hilt будет готов к использованию в ваших тестах, вы сможете использовать несколько функций для настройки процесса тестирования.

Типы внедрения в тестах

Чтобы внедрить типы в тест, используйте @Inject для внедрения полей. Чтобы указать Hilt заполнить поля @Inject , вызовите hiltRule.inject() .

См. следующий пример инструментального теста:

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

Заменить привязку

Если вам нужно внедрить поддельный или имитационный экземпляр зависимости, вам нужно указать Hilt не использовать привязку, которую она использовала в рабочем коде, а вместо этого использовать другую. Чтобы заменить привязку, вам необходимо заменить модуль, содержащий привязку, на тестовый модуль, содержащий привязки, которые вы хотите использовать в тесте.

Например, предположим, что ваш производственный код объявляет привязку для AnalyticsService следующим образом:

@Module
@InstallIn(SingletonComponent::class)
abstract class AnalyticsModule {

  @Singleton
  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}
@Module
@InstallIn(SingletonComponent.class)
public abstract class AnalyticsModule {

  @Singleton
  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    AnalyticsServiceImpl analyticsServiceImpl
  );
}

Чтобы заменить привязку AnalyticsService в тестах, создайте новый модуль Hilt в папке test или androidTest с поддельной зависимостью и аннотируйте его @TestInstallIn . Вместо этого во все тесты в этой папке вводятся поддельные зависимости.

@Module
@TestInstallIn(
    components = [SingletonComponent::class],
    replaces = [AnalyticsModule::class]
)
abstract class FakeAnalyticsModule {

  @Singleton
  @Binds
  abstract fun bindAnalyticsService(
    fakeAnalyticsService: FakeAnalyticsService
  ): AnalyticsService
}
@Module
@TestInstallIn(
    components = SingletonComponent.class,
    replaces = AnalyticsModule.class
)
public abstract class FakeAnalyticsModule {

  @Singleton
  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    FakeAnalyticsService fakeAnalyticsService
  );
}

Замена привязки за один тест

Чтобы заменить привязку в одном тесте, а не во всех тестах, удалите модуль Hilt из теста с помощью аннотации @UninstallModules и создайте новый тестовый модуль внутри теста.

Следуя примеру AnalyticsService из предыдущей версии, начните с указания Hilt игнорировать рабочий модуль, используя аннотацию @UninstallModules в тестовом классе:

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

Далее необходимо заменить обвязку. Создайте новый модуль в тестовом классе, который определяет привязку теста:

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

  @Module
  @InstallIn(SingletonComponent::class)
  abstract class TestModule {

    @Singleton
    @Binds
    abstract fun bindAnalyticsService(
      fakeAnalyticsService: FakeAnalyticsService
    ): AnalyticsService
  }

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

  @Module
  @InstallIn(SingletonComponent.class)
  public abstract class TestModule {

    @Singleton
    @Binds
    public abstract AnalyticsService bindAnalyticsService(
      FakeAnalyticsService fakeAnalyticsService
    );
  }
  ...
}

Это заменяет привязку только для одного тестового класса. Если вы хотите заменить привязку для всех тестовых классов, используйте аннотацию @TestInstallIn из раздела выше. Альтернативно вы можете поместить привязку теста в test модуль для тестов Robolectric или в модуль androidTest для инструментальных тестов. Рекомендуется использовать @TestInstallIn когда это возможно.

Привязка новых ценностей

Используйте аннотацию @BindValue чтобы легко связать поля вашего теста с графом зависимостей Hilt. Добавьте к полю аннотацию @BindValue , и оно будет привязано к объявленному типу поля с любыми квалификаторами, которые присутствуют для этого поля.

В примере AnalyticsService вы можете заменить AnalyticsService поддельным, используя @BindValue :

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

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

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

  @BindValue AnalyticsService analyticsService = FakeAnalyticsService();

  ...
}

Это упрощает как замену привязки, так и ссылку на привязку в тесте, позволяя делать и то, и другое одновременно.

@BindValue работает с квалификаторами и другими аннотациями тестирования. Например, если вы используете библиотеки тестирования, такие как Mockito , вы можете использовать их в тесте Robolectric следующим образом:

...
class SettingsActivityTest {
  ...

  @BindValue @ExampleQualifier @Mock
  lateinit var qualifiedVariable: ExampleCustomType

  // Robolectric tests here
}
...
class SettingsActivityTest {
  ...
  @BindValue @ExampleQualifier @Mock ExampleCustomType qualifiedVariable;

  // Robolectric tests here
}

Если вам нужно добавить мультипривязку , вы можете использовать аннотации @BindValueIntoSet и @BindValueIntoMap вместо @BindValue . @BindValueIntoMap требует, чтобы вы также аннотировали поле аннотацией ключа карты.

Особые случаи

Hilt также предоставляет функции для поддержки нестандартных случаев использования.

Пользовательское приложение для тестов

Если вы не можете использовать HiltTestApplication поскольку вашему тестовому приложению необходимо расширить другое приложение, аннотируйте новый класс или интерфейс с помощью @CustomTestApplication , передав значение базового класса, который вы хотите расширить сгенерированное приложение Hilt.

@CustomTestApplication сгенерирует класс Application , готовый к тестированию с помощью Hilt, который расширяет приложение, которое вы передали в качестве параметра.

@CustomTestApplication(BaseApplication::class)
interface HiltTestApplication
@CustomTestApplication(BaseApplication.class)
interface HiltTestApplication { }

В этом примере Hilt создает Application с именем HiltTestApplication_Application , которое расширяет класс BaseApplication . Обычно имя созданного приложения — это имя аннотированного класса, к которому добавляется _Application . Вы должны настроить созданное тестовое приложение Hilt для запуска в ваших инструментальных тестах или тестах Robolectric , как описано в разделе Тестовое приложение .

Несколько объектов TestRule в инструментированном тесте

Если в вашем тесте есть другие объекты TestRule , существует несколько способов убедиться, что все правила работают вместе.

Вы можете объединить правила следующим образом:

@HiltAndroidTest
class SettingsActivityTest {

  @get:Rule
  var rule = RuleChain.outerRule(HiltAndroidRule(this)).
        around(SettingsActivityTestRule(...))

  // UI tests here.
}
@HiltAndroidTest
public final class SettingsActivityTest {

  @Rule public RuleChain rule = RuleChain.outerRule(new HiltAndroidRule(this))
        .around(new SettingsActivityTestRule(...));

  // UI tests here.
}

Альтернативно вы можете использовать оба правила на одном уровне, если HiltAndroidRule выполняется первым. Укажите порядок выполнения, используя атрибут order в аннотации @Rule . Это работает только в JUnit версии 4.13 или выше:

@HiltAndroidTest
class SettingsActivityTest {

  @get:Rule(order = 0)
  var hiltRule = HiltAndroidRule(this)

  @get:Rule(order = 1)
  var settingsActivityTestRule = SettingsActivityTestRule(...)

  // UI tests here.
}
@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 из библиотеки androidx.fragment:fragment-testing с Hilt, поскольку она опирается на действие, не помеченное @AndroidEntryPoint .

Вместо этого используйте код launchFragmentInHiltContainer из репозитория architecture-samples GitHub.

Используйте точку входа до того, как будет доступен одноэлементный компонент.

Аннотация @EarlyEntryPoint предоставляет запасной выход, когда необходимо создать точку входа Hilt до того, как одноэлементный компонент станет доступен в тесте Hilt.

Дополнительная информация о @EarlyEntryPoint в документации Hilt .