Primary/Android

[Android Fundamentals] Activity - 5. Activity test

해스끼 2024. 7. 19. 11:57

안드로이드 앱에서는 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되면 메모리 누수가 발생할 수도 있기 때문.

참고자료

Test your app's activities  |  Android Developers

 

앱 활동 테스트  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 앱 활동 테스트 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 활동은 앱 내 모든 사용자 상호작용의

developer.android.com