[Android Fundamentals] Activity - 5. Activity test
안드로이드 앱에서는 UI = activity라고 봐도 될 정도로 activity의 비중이 크다. 그래서 activity가 실제 기기에서 어떻게 동작하는지 잘 테스트해야 한다.
- 전화가 왔을 때처럼 다른 앱이 activity를 interrupt할 때
- 시스템이 activity를 destroy하고 다시 만들 때
- 사용자가 activity를 PIP나 멀티 윈도우에서 사용할 때
등의 다양한 경우에서 activity가 생명주기 이벤트에 잘 대응하는지 확인해야 한다.
Activity state 설정
Activity를 테스트할 때에는 activity의 state를 설정하는 것이 중요하다. 테스트의 given-when-then 패턴에서 "given" 부분은 AndroidX Test 라이브러리의 ``ActivityScenario`` 인스턴스를 사용하여 정의할 수 있다. ``ActivityScenario``를 사용하면 디바이스 레벨 이벤트를 시뮬레이션할 수 있다.
``ActivityScenario``는 로컬 단위 테스트와 기기 통합 테스트에서 모두 사용할 수 있다. 실제 기기와 에뮬레이터에서는 thread safety, 스레드 간 이벤트 동기화 등의 기능을 지원한다.
특히 activity가 destroy 혹은 create될 때 어떻게 동작하는지 테스트하는 데 특화되어 있다고 한다. 이 문단에서 ``ActivityScenario``의 사용 예시를 알아보자.
Activity 생성
테스트 코드에서 activity는 이렇게 만들 수 있다.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEvent() {
launchActivity<MyActivity>().use {
}
}
}
Activity가 생성되면 ``ActivityScenario``는 activity의 상태를 Resumed로 바꾼다. Resumed는 UI가 사용자와 상호작용하는 상태를 의미하므로, activity의 ``View`` 객체를 테스트할 수 있다.
Activity를 launch한 후에는 ``close()`` 함수를 실행하는 것이 좋다. 테스트에서 사용한 리소스를 청소하는 과정이라고 보면 된다. ``ActivityScenario``는 ``Closeable`` 인터페이스를 상속받기 때문에 ``use`` 함수를 사용하여 ``close()`` 함수를 자동으로 호출할 수 있다.
Activity 상태 변경
Activity의 상태를 바꾸고 싶다면 ``moveToState()`` 함수를 호출한다.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEvent() {
launchActivity<MyActivity>().use { scenario ->
scenario.moveToState(State.CREATED)
}
}
}
Activity의 상태 확인
Activity의 state를 확인하고 싶다면 ``ActivityScenario.state`` 필드를 사용할 수 있다. 다른 activity로 이동하거나, activity가 destroy되는 상황을 확인할 수 있다.
@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
}
}
}
Activity 재생성
메모리가 부족할 때 activity가 destroy될 수 있다. ``recreate()``를 호출하면 이 상황을 시뮬레이션할 수 있다.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEvent() {
launchActivity<MyActivity>().use { scenario ->
scenario.recreate()
}
}
}
``ActivityScenario``가 activity의 saved instance를 자동으로 백업/복구한다.
Activity의 결과값 얻기
Activity가 result를 반환하는 경우, ``ActivityScenario.result`` 필드에서 확인할 수 있다.
@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
}
}
}
Activity에서 이벤트 실행
Activity에서 이벤트를 실행하려면, view를 찾아서 액션을 수행하면 된다.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEvent() {
launchActivity<MyActivity>().use {
// 클릭 이벤트
onView(withId(R.id.refresh)).perform(click())
}
}
}
Activity 자체의 이벤트는 다음과 같이 실행할 수 있다.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEvent() {
launchActivity<MyActivity>().use { scenario ->
scenario.onActivity { activity ->
activity.handleSwipeToRefresh()
}
}
}
}
참고로, ``ActivityScenario.onActivity()`` 함수 안에서 사용하는 객체를 ``onActivity()`` 외부에서 참조하면 안 된다. 시스템 자원을 소모하기도 하고, activity가 recreate되면 메모리 누수가 발생할 수도 있기 때문.