Действия служат контейнерами для каждого взаимодействия пользователя с вашим приложением, поэтому важно протестировать, как действия вашего приложения ведут себя во время событий на уровне устройства, таких как:
- Другое приложение, например приложение телефона на устройстве, прерывает работу вашего приложения.
- Система уничтожает и воссоздает вашу активность.
- Пользователь помещает вашу активность в новую оконную среду, например, «картинка в картинке» (PIP) или многооконный режим.
В частности, важно убедиться, что ваша деятельность корректно реагирует на события, описанные в разделе «Жизненный цикл деятельности» .
В этом руководстве описывается, как оценить способность вашего приложения сохранять целостность данных и обеспечивать удобство использования по мере перехода действий вашего приложения через различные состояния в его жизненном цикле.
Управлять состоянием активности
Одним из ключевых аспектов тестирования активности приложения является размещение активности в определённых состояниях. Чтобы определить эту «заданную» часть тестов, используйте экземпляры класса ActivityScenario из библиотеки AndroidX Test . Используя этот класс, вы можете размещать активность в состояниях, имитирующих события на уровне устройства.
ActivityScenario — это кроссплатформенный API, который можно использовать как в локальных модульных тестах, так и в интеграционных тестах на устройстве. На реальном или виртуальном устройстве ActivityScenario обеспечивает потокобезопасность, синхронизируя события между инструментальным потоком теста и потоком, выполняющим тестируемую активность.
Этот API особенно хорошо подходит для оценки поведения тестируемой активности при её уничтожении или создании. В этом разделе представлены наиболее распространённые примеры использования этого API.
Создать занятие
Чтобы создать тестируемое действие, добавьте код, показанный в следующем фрагменте:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEvent() { launchActivity<MyActivity>().use { } } }
После создания активности ActivityScenario переводит её в состояние RESUMED . Это состояние означает, что ваша активность выполняется и видна пользователям. В этом состоянии вы можете свободно взаимодействовать с элементами View вашей активности, используя тесты пользовательского интерфейса Espresso .
Google рекомендует вызывать метод close для активности после завершения теста. Это освобождает связанные ресурсы и повышает стабильность тестов. ActivityScenario реализует Closeable , поэтому вы можете применить расширение use или try-with-resources в языке программирования Java, чтобы активность автоматически закрывалась.
В качестве альтернативы вы можете использовать ActivityScenarioRule для автоматического вызова ActivityScenario.launch перед каждым тестом и ActivityScenario.close при завершении теста. В следующем примере показано, как определить правило и получить экземпляр сценария из него:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @get:Rule var activityScenarioRule = activityScenarioRule<MyActivity>() @Test fun testEvent() { val scenario = activityScenarioRule.scenario } }
Перевести активность в новое состояние
Чтобы перевести активность в другое состояние, например, CREATED или STARTED , вызовите метод moveToState() . Это действие имитирует ситуацию, когда активность остановлена или приостановлена соответственно из-за прерывания другим приложением или системным действием.
Пример использования moveToState() представлен в следующем фрагменте кода:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEvent() { launchActivity<MyActivity>().use { scenario -> scenario.moveToState(State.CREATED) } } }
Определить текущее состояние активности
Чтобы определить текущее состояние тестируемой активности, получите значение поля state в объекте ActivityScenario . Особенно полезно проверять состояние тестируемой активности, если она перенаправляется на другую активность или завершается сама, как показано в следующем фрагменте кода:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEvent() { launchActivity<MyActivity>().use { scenario -> scenario.onActivity { activity -> startActivity(Intent(activity, MyOtherActivity::class.java)) } val originalActivityState = scenario.state } } }
Воссоздайте активность
Когда на устройстве мало ресурсов, система может удалить активность, требуя от вашего приложения её повторного создания при возвращении пользователя в приложение. Чтобы смоделировать эти условия, вызовите recreate() :
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEvent() { launchActivity<MyActivity>().use { scenario -> scenario.recreate() } } }
Класс ActivityScenario хранит сохранённое состояние экземпляра активности и все объекты, аннотированные с помощью @NonConfigurationInstance . Эти объекты загружаются в новый экземпляр тестируемой активности.
Получить результаты активности
Чтобы получить код результата или данные, связанные с завершенным действием, получите значение поля result в объекте ActivityScenario , как показано в следующем фрагменте кода:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testResult() { launchActivity<MyActivity>().use { onView(withId(R.id.finish_button)).perform(click()) // Activity under test is now finished. val resultCode = scenario.result.resultCode val resultData = scenario.result.resultData } } }
Действия триггера в активности
Все методы в ActivityScenario являются блокирующими вызовами, поэтому API требует, чтобы вы запускали их в потоке инструментария.
Чтобы инициировать действия в тестируемой активности, используйте сопоставления представлений Espresso для взаимодействия с элементами в вашем представлении:
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEvent() { launchActivity<MyActivity>().use { onView(withId(R.id.refresh)).perform(click()) } } }
Однако, если вам необходимо вызвать метод самой активности, вы можете сделать это безопасно, реализовав ActivityAction :
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEvent() { launchActivity<MyActivity>().use { scenario -> scenario.onActivity { activity -> activity.handleSwipeToRefresh() } } } }