액티비티는 앱 내 모든 사용자 상호작용의 컨테이너로 사용되므로 디바이스 수준 이벤트가 발생했을 때 액티비티가 어떻게 동작하는지 테스트하는 것이 중요하다. 해당 디바이스 수준 이벤트는 다음과 같다.
- 다른 앱이 앱의 액티비티를 방해한다.
- 시스템이 액티비티를 소멸시키고 재생성한다.
- 사용자가 액티비티를 PIP/멀티 윈도우 모드와 같은 새 윈도우 처리 환경에 배치한다.
특히 액티비티가 수명 주기 포스팅에서 설명한 이벤트에 올바르게 동작하도록 하는 것이 중요하다.
이 가이드에서는 앱이 수명주기 상태가 전환될 때 데이터 통합과 뛰어난 UX를 유지하는 능력을 평가하는 방법을 설명한다.
액티비티 상태 변경
액티비티 테스트의 가장 중요한 측면 중 하나는 앱의 액티비티를 특정 상태에 배치하는 것이다. 이러한 "특정 요소"를 테스트에 정의하려면 ActivityScenario
인스턴스를 사용해야 한다. 이는 AndroidX 테스트 라이브러리에 포함되어 있다. 이 클래스를 사용하면 디바이스 수준 이벤트를 시뮬레이션하는 상태에 액티비티를 배치할 수 있다.
ActivityScenario
는 로컬 유닛 테스트와 디바이스 내 통합 테스트에서 모두 사용할 수 있는 크로스 플랫폼 API이다. 실제 또는 가상 디바이스에서 ActivityScenario
는 스레드 안전성을 제공하여 테스트 계측 스레드와 테스트 중인 액티비티를 실행하는 스레드 간 이벤트를 동기화한다. 또한 이 API는 테스트 중인 액티비티가 소멸되거나 생성될 때 테스트에서 어떻게 동작하는지 판단하는 데 특히 적합하다.
이 섹션에서는 이 API와 관련된 가장 일반적인 사용 사례를 보여준다.
액티비티 생성
테스트에서 액티비티를 생성하려면 다음 스니펫의 코드를 추가해야 한다.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEvent() {
launchActivity<MyActivity>().use {
}
}
}
액티비티를 생성한 후 ActivityScenario
는 액티비티를 재개됨 상태로 전환한다. 이 상태는 액티비티가 실행 중이고 사용자에게 보인다는 것을 나타낸다. 이 상태에서 Espresso UI 테스트를 사용하여 액티비티의 뷰와 자유롭게 상호작용할 수 있다.
테스트를 마쳤을 때 액티비티에서 close()
를 호출하기를 강력히 권장한다. 이는 액티비티와 관련된 리소스를 정리하고 테스트의 안정성을 향상한다. ActivityScenario
는 Closeable
인터페이스를 구현하므로 확장 함수인 use()를 사용하여 액티비티가 자동적으로 닫히도록 할 수 있다.
또는 이 대신 ActivityScenarioRule
를 사용하여 각 테스트 이전에 자동적으로 ActivityScenario.launch()
를 호출하고, 테스트가 끝날 때 ActivityScenario.close()
를 호출할 수 있다. 아래 예시는 규칙을 정의하고 이 규칙에서 시나리오 인스턴스를 가져오는 방법을 보여준다.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@get:Rule var activityScenarioRule = activityScenarioRule<MyActivity>()
@Test fun testEvent() {
val scenario = activityScenarioRule.scenario
}
}
새 액티비티 상태로 전환
액티비티를 생성됨 또는 시작됨과 같은 다른 상태로 전환하려면 moveToState()
를 호출해야 한다. 이 액션은 액티비티가 다른 앱이나 시스템 액션에 의해 방해받아서 중단되거나 일시중지되는 각각의 경우를 시뮬레이션한다.
moveToState()
를 사용하는 예시는 다음과 같다.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEvent() {
launchActivity<MyActivity>().use { scenario ->
scenario.moveToState(State.CREATED)
}
}
}
테스트 중인 액티비티를 현재 상태로 전환하려 시도하면 ActivityScenario
는 이 요청을 예외가 아닌 무작동으로 처리한다.
현재 활동 상태 확인
테스트 중인 액티비티의 현재 상태를 확인하려면 ActivityScenario
객체의 state
필드의 값을 가져와야 한다. 특히 다음 코드 스니펫처럼, 테스트 중인 액티비티가 다른 액티비티로 리디렉션되거나 자신을 종료할 때 상태를 점검하는 것은 도움이 된다.
@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
어노테이션을 사용하는 모든 객체를 유지한다. 이 객체들은 테스트 중인 액티비티의 새 인스턴스에 로드된다.
액티비티 결과 가져오기
종료되는 액티비티와 관련된 데이터나 결과 코드를 가져오려면 ActivityScenario
객체의 result
필드의 값을 가져와야 한다.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testResult() {
launchActivity<MyActivity>().use {
onView(withId(R.id.finish_button)).perform(click())
// 테스트 중인 액티비티가 종료됨
val resultCode = scenario.result.resultCode
val resultData = scenario.result.resultData
}
}
}
액티비티 안에서 액션 발생시키기
ActivityScenario
의 모든 메서드는 블로킹 메서드이므로 계측 스레드에서 실행해야 한다.
테스트 중인 액티비티에서 액션을 시작하려면 Espresso view matcher를 사용하여 뷰 안의 요소와 상호작용해야 한다.
@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()
}
}
}
}
테스트 클래스에서 onActivity()
에 전달되는 객체를 계속 참조하면 안 된다. 이러한 참조는 시스템 리소스를 소모하며 프레임워크가 콜백 메서드에 전달되는 액티비티를 재생성할 수 있기 때문에 오래된 것일 수 있다.
'Android' 카테고리의 다른 글
액티비티 (6) : 프로세스와 앱 수명 주기 (0) | 2022.05.12 |
---|---|
액티비티 (5) : 태스크와 백 스택 (0) | 2022.05.11 |
액티비티 (3) : 액티비티 상태 변화 관리 (0) | 2022.04.08 |
액티비티 (2) : 액티비티의 수명 주기 (0) | 2022.04.08 |
액티비티 (1) : 액티비티 만들기 (0) | 2022.04.06 |