В этой статье описывается, как включить API, предоставляемые фреймворком, в тесты, оценивающие поведение каждого фрагмента.
Фрагменты служат многоразовыми контейнерами в приложении, позволяя представлять один и тот же макет пользовательского интерфейса в различных действиях и конфигурациях макета. Учитывая универсальность фрагментов, важно убедиться, что они обеспечивают согласованную и эффективную работу с ресурсами. объявлений.
- Фрагмент не должен зависеть от определенного родительского действия или фрагмента.
- Не создавайте иерархию View фрагмента, если он не виден пользователю.
Чтобы упростить настройку условий для выполнения этих тестов, библиотека AndroidX fragment-testing предоставляет класс FragmentScenario для создания фрагментов и изменения их Lifecycle.State.
Объявление зависимостей
Чтобы использовать FragmentScenario, определите артефакт fragment-testing-manifest в файле build.gradle приложения с помощью debugImplementation, а артефакт fragment-testing – с помощью androidTestImplementation, как показано в примере ниже.
классный
dependencies { def fragment_version = "1.9.1" debugImplementation "androidx.fragment:fragment-testing-manifest:$fragment_version" androidTestImplementation "androidx.fragment:fragment-testing:$fragment_version" }
Котлин
dependencies { val fragment_version = "1.9.1" 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.
В следующих примерах кода показано, как запустить фрагмент с помощью каждого метода:
Пример launchInContainer()
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
// The "fragmentArgs" argument is optional.
val fragmentArgs = bundleOf(“selectedListItem” to 0)
val scenario = launchFragmentInContainer<EventFragment>(fragmentArgs)
...
}
}
Пример launch()
@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 ViewMatchers для взаимодействия с элементами в представлении:
@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())
}
}