Бездействующие ресурсы Espresso

Бездействующий ресурс представляет собой асинхронную операцию, результаты которой влияют на последующие операции в тестировании пользовательского интерфейса. Регистрация бездействующих ресурсов в Espresso позволяет более надежно проверять асинхронные операции при тестировании приложения.

Как определить, когда нужны бездействующие ресурсы

Espresso предоставляет сложный набор возможностей синхронизации. Однако эта характеристика фреймворка применяется только к операциям, которые публикуют сообщения в MessageQueue, например к подклассу View, который рисует свое содержимое на экране.

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

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

  • Добавление вызовов объявлений в Thread.sleep(). Если вы добавите искусственные задержки в тесты, то на выполнение вашего набора тестов будет уходить больше времени, а на медленных устройствах они могут по-прежнему завершаться с ошибкой. Кроме того, такие задержки плохо масштабируются, поскольку в будущих выпусках приложению может потребоваться выполнять более трудоемкую асинхронную работу.
  • Реализация оберток повторных попыток. Они используют цикл, чтобы многократно проверять, выполняет ли приложение асинхронную работу, пока не истечет время ожидания. Даже если вы укажете максимальное количество повторных попыток в своих тестах, каждое повторное выполнение потребляет системные ресурсы, особенно ЦП.
  • Использование экземпляров CountDownLatch, которые позволяют одному или нескольким потокам ожидать завершения определенного количества операций, выполняемых в другом потоке. Для этих объектов необходимо указать время ожидания, иначе приложение может быть заблокировано на неопределенный срок. Кроме того, защелки усложняют код и затрудняют его обслуживание.

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

Примеры использования

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

  • Загрузка данных из интернета или локального источника.
  • Устанавливать соединения с базами данных и обратными вызовами.
  • Управление сервисами с помощью системного сервиса или экземпляра IntentService.
  • Выполнение сложной бизнес-логики, например преобразования растровых изображений.

Это особенно важно, если такие операции обновляют интерфейс, который затем проверяется тестами.

Примеры реализации бездействующих ресурсов

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

CountingIdlingResource
Поддерживает счетчик активных задач. Когда счетчик равен нулю, связанный с ним ресурс считается неиспользуемым. Эта функция очень похожа на Semaphore. В большинстве случаев этого достаточно для управления асинхронной работой приложения во время тестирования.
UriIdlingResource
Аналогично CountingIdlingResource, но счетчик должен быть равен нулю в течение определенного периода времени, прежде чем ресурс будет считаться неиспользуемым. Этот дополнительный период ожидания учитывает последовательные сетевые запросы, когда приложение в вашем потоке может сделать новый запрос сразу после получения ответа на предыдущий.
IdlingThreadPoolExecutor
Специальная реализация ThreadPoolExecutor, которая отслеживает общее количество выполняемых задач в созданных пулах потоков. Этот класс использует CountingIdlingResource для подсчета активных задач.
IdlingScheduledThreadPoolExecutor
Специальная реализация ScheduledThreadPoolExecutor. Он предоставляет те же функции и возможности, что и класс IdlingThreadPoolExecutor, но также может отслеживать задачи, запланированные на будущее или на периодическое выполнение.

Как создать собственный бездействующий ресурс

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

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

Вызывайте переходы в режим ожидания вне проверок на бездействие.
После того как приложение перейдет в режим ожидания, вызовите onTransitionToIdle() вне реализаций isIdleNow(). Это позволит Espresso не выполнять повторную проверку, чтобы определить, бездействует ли ресурс.

Ниже приведен фрагмент кода, соответствующий этой рекомендации.

Kotlin

fun isIdle() {
    // DON'T call callback.onTransitionToIdle() here!
}

fun backgroundWorkDone() {
    // Background work finished.
    callback.onTransitionToIdle() // Good. Tells Espresso that the app is idle.

    // Don't do any post-processing work beyond this point. Espresso now
    // considers your app to be idle and moves on to the next test action.
}

Java

public void isIdle() {
    // DON'T call callback.onTransitionToIdle() here!
}

public void backgroundWorkDone() {
    // Background work finished.
    callback.onTransitionToIdle() // Good. Tells Espresso that the app is idle.

    // Don't do any post-processing work beyond this point. Espresso now
    // considers your app to be idle and moves on to the next test action.
}
Регистрируйте бездействующие ресурсы до того, как они вам понадобятся.

Преимущества синхронизации, связанные с бездействующими ресурсами, вступают в силу только после первого вызова Espresso метода isIdleNow() этого ресурса.

Ниже приведены примеры этого свойства.

  • Если вы зарегистрируете бездействующий ресурс в методе с аннотацией @Before, он будет задействован в первой строке каждого теста.
  • Если вы зарегистрируете бездействующий ресурс в тесте, он начнет действовать во время следующего действия на основе Espresso. Это происходит, даже если следующее действие находится в том же тесте, что и инструкция, регистрирующая бездействующий ресурс.
Отменяйте регистрацию бездействующих ресурсов, когда они больше не нужны.

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

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

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

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

Например, бездействующие ресурсы, которые вы реализуете и регистрируете, не должны содержать ссылки на объекты View.

Регистрация бездействующих ресурсов

Espresso предоставляет класс контейнера, в который можно поместить бездействующие ресурсы приложения. Этот класс, который называется IdlingRegistry, представляет собой автономный артефакт, который минимально влияет на работу приложения. Он также позволяет выполнить следующие действия, чтобы упростить обслуживание приложения:

  • В тестах приложения создайте ссылку на IdlingRegistry, а не на содержащиеся в нем бездействующие ресурсы.
  • Поддерживайте различия в наборе бездействующих ресурсов, которые вы используете для каждого варианта сборки.
  • Определяйте бездействующие ресурсы в службах приложения, а не в компонентах пользовательского интерфейса, которые ссылаются на эти службы.

Как интегрировать бездействующие ресурсы в приложение

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

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

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

Альтернативные подходы

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

  • Создайте варианты сборки, например версии продукта Gradle, и используйте бездействующие ресурсы только в отладочной сборке приложения.
  • Используйте фреймворк внедрения зависимостей, например Dagger, чтобы внедрить граф зависимостей бездействующего ресурса приложения в тесты. Если вы используете Dagger 2, сама инъекция должна исходить из подкомпонента.
  • Реализуйте бездействующий ресурс в тестах приложения и предоставьте доступ к той части реализации приложения, которую необходимо синхронизировать в этих тестах.

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

Дополнительные ресурсы

Дополнительную информацию об использовании Espresso в тестах Android можно найти в следующих ресурсах.

Образцы