В этом разделе описывается, как включать API-интерфейсы, предоставляемые платформой, в тесты, оценивающие поведение каждого фрагмента.
Фрагменты служат контейнерами многократного использования в вашем приложении, позволяя вам представлять один и тот же макет пользовательского интерфейса в различных действиях и конфигурациях макета. Учитывая универсальность фрагментов, важно убедиться, что они обеспечивают согласованность и эффективность использования ресурсов. Обратите внимание на следующее:
- Ваш фрагмент не должен зависеть от конкретного родительского действия или фрагмента.
- Не следует создавать иерархию представлений фрагмента, если фрагмент не виден пользователю.
Чтобы помочь настроить условия для выполнения этих тестов, библиотека fragment-testing
AndroidX предоставляет класс FragmentScenario
для создания фрагментов и изменения их Lifecycle.State
.
Объявление зависимостей
Чтобы использовать FragmentScenario
определите артефакт fragment-testing-manifest
в файле build.gradle
вашего приложения с помощью debugImplementation
и артефакт fragment-testing
с помощью androidTestImplementation
, как показано в следующем примере:
Groovy
dependencies { def fragment_version = "1.8.5" debugImplementation "androidx.fragment:fragment-testing-manifest:$fragment_version" androidTestImplementation "androidx.fragment:fragment-testing:$fragment_version" }
Kotlin
dependencies { val fragment_version = "1.8.5" debugImplementation("androidx.fragment:fragment-testing-manifest:$fragment_version") androidTestImplementation("androidx.fragment:fragment-testing:$fragment_version") }
В примерах тестирования на этой странице используются утверждения из библиотек Espresso и Truth . Информацию о других доступных библиотеках тестирования и утверждений см. в разделе Настройка проекта для AndroidX Test .
Создать фрагмент
FragmentScenario
включает в себя следующие методы для запуска фрагментов в тестах:
-
launchInContainer()
для тестирования пользовательского интерфейса фрагмента.FragmentScenario
прикрепляет фрагмент к контроллеру корневого представления действия. В противном случае эта содержащая активность пуста. -
launch()
для тестирования без пользовательского интерфейса фрагмента.FragmentScenario
прикрепляет этот тип фрагмента к пустой активности , у которой нет корневого представления.
После запуска одного из этих типов фрагментов FragmentScenario
переводит тестируемый фрагмент в заданное состояние. По умолчанию это состояние RESUMED
, но вы можете переопределить его с помощью аргумента initialState
. Состояние RESUMED
указывает, что фрагмент запущен и виден пользователю. Оценить информацию о его элементах пользовательского интерфейса можно с помощью тестов Espresso UI .
Следующие примеры кода показывают, как запустить фрагмент с помощью каждого метода:
пример запускаInContainer()
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
// The "fragmentArgs" argument is optional.
val fragmentArgs = bundleOf(“selectedListItem” to 0)
val scenario = launchFragmentInContainer<EventFragment>(fragmentArgs)
...
}
}
пример запуска()
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
// The "fragmentArgs" arguments are optional.
val fragmentArgs = bundleOf("numElements" to 0)
val scenario = launchFragment<EventFragment>(fragmentArgs)
...
}
}
Предоставление зависимостей
Если у ваших фрагментов есть зависимости, вы можете предоставить тестовые версии этих зависимостей, предоставив специальную FragmentFactory
для методов launchInContainer()
или launch()
.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val someDependency = TestDependency()
launchFragmentInContainer {
EventFragment(someDependency)
}
...
}
}
Дополнительные сведения об использовании FragmentFactory
для предоставления зависимостей фрагментам см. в разделе Диспетчер фрагментов .
Привести фрагмент в новое состояние
В тестах пользовательского интерфейса вашего приложения обычно достаточно запустить тестируемый фрагмент и начать его тестирование из состояния RESUMED
. Однако в более детальных модульных тестах вы также можете оценить поведение фрагмента при его переходе из одного состояния жизненного цикла в другое. Вы можете указать начальное состояние, передав аргумент initialState
любой из функций launchFragment*()
.
Чтобы перевести фрагмент в другое состояние жизненного цикла, вызовите moveToState()
. Этот метод поддерживает следующие состояния в качестве аргументов: CREATED
, STARTED
, RESUMED
и DESTROYED
. Этот метод имитирует ситуацию, когда фрагмент или действие, содержащее ваш фрагмент, по какой-либо причине меняет свое состояние.
В следующем примере тестовый фрагмент запускается в состоянии INITIALIZED
, а затем перемещается в состояние RESUMED
:
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<EventFragment>(
initialState = Lifecycle.State.INITIALIZED
)
// EventFragment has gone through onAttach(), but not onCreate().
// Verify the initial state.
scenario.moveToState(Lifecycle.State.RESUMED)
// EventFragment moves to CREATED -> STARTED -> RESUMED.
...
}
}
Воссоздать фрагмент
Если ваше приложение работает на устройстве с низким уровнем ресурсов, система может уничтожить активность, содержащую ваш фрагмент. В этой ситуации ваше приложение должно воссоздать фрагмент, когда пользователь вернется к нему. Чтобы смоделировать эту ситуацию, вызовите recreate()
:
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<EventFragment>()
scenario.recreate()
...
}
}
FragmentScenario.recreate()
уничтожает фрагмент и его хост, а затем воссоздает их. Когда класс FragmentScenario
воссоздает тестируемый фрагмент, он возвращается в состояние жизненного цикла, в котором он находился до уничтожения.
Взаимодействие с фрагментами пользовательского интерфейса
Чтобы инициировать действия пользовательского интерфейса в тестируемом фрагменте, используйте средства сопоставления представлений Espresso для взаимодействия с элементами в вашем представлении:
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<EventFragment>()
onView(withId(R.id.refresh)).perform(click())
// Assert some expected behavior
...
}
}
Если вам нужно вызвать метод самого фрагмента, например, ответить на выбор в меню параметров, вы можете сделать это безопасно, получив ссылку на фрагмент с помощью FragmentScenario.onFragment()
и передав FragmentAction
:
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<EventFragment>()
scenario.onFragment { fragment ->
fragment.myInstanceMethod()
}
}
}
Действия диалогового окна тестирования
FragmentScenario
также поддерживает тестирование фрагментов диалогов . Хотя фрагменты диалогов содержат элементы пользовательского интерфейса, их макет заполняется в отдельном окне, а не в самой активности. По этой причине используйте FragmentScenario.launch()
для проверки фрагментов диалога.
В следующем примере тестируется процесс закрытия диалога:
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testDismissDialogFragment() {
// Assumes that "MyDialogFragment" extends the DialogFragment class.
with(launchFragment<MyDialogFragment>()) {
onFragment { fragment ->
assertThat(fragment.dialog).isNotNull()
assertThat(fragment.requireDialog().isShowing).isTrue()
fragment.dismiss()
fragment.parentFragmentManager.executePendingTransactions()
assertThat(fragment.dialog).isNull()
}
}
// Assumes that the dialog had a button
// containing the text "Cancel".
onView(withText("Cancel")).check(doesNotExist())
}
}