앱 활동 테스트

활동은 앱 내 모든 사용자 상호작용의 컨테이너 역할을 하므로 다음과 같은 기기 수준 이벤트가 발생하는 동안 앱의 활동이 어떻게 동작하는지 테스트하는 것이 중요합니다.

  • 기기의 스마트폰 앱과 같은 다른 앱이 앱의 활동을 중단시킵니다.
  • 시스템이 활동을 제거하고 다시 생성합니다.
  • 사용자가 PIP 모드 또는 멀티 윈도우와 같은 새로운 윈도우 처리 환경에 활동을 배치합니다.

특히 활동이 활동 수명 주기에 설명된 이벤트에 응답하여 올바르게 동작하는지 확인하는 것이 중요합니다.

이 가이드에서는 앱의 활동이 수명 주기의 다양한 상태를 통해 전환될 때 앱이 데이터 무결성 및 우수한 사용자 환경을 유지하는 기능을 평가하는 방법을 설명합니다.

Compose에서 활동 테스트

Jetpack Compose로 빌드된 앱을 테스트할 때는 일반적으로 createAndroidComposeRule를 사용하여 활동을 실행하고 UI 구성요소와 상호작용합니다.

하지만 구성 변경이나 활동이 백그라운드로 전환되거나 시스템에 의해 소멸되는 것과 같은 기기 수준 이벤트를 테스트하려면 활동의 수명 주기를 직접 조작해야 합니다. 이렇게 하려면 기본 ActivityScenario 프레임워크를 사용합니다.

Compose 테스트 규칙은 이 시나리오를 자동으로 래핑하고 관리합니다. 이 가이드에서는 최신 UI 테스트와 표준 수명 주기 관리 간의 격차를 해소하기 위해 다음 패턴을 사용합니다.

@get:Rule
val composeTestRule = createAndroidComposeRule<MyActivity>()

@Test fun testEvent() {
    val scenario = composeTestRule.activityRule.scenario

    // ...
}

활동 상태 변경

앱 활동 테스트의 한 가지 주요 측면은 앱 활동을 특정 상태에 배치하는 것입니다. 테스트의 이 '특정' 부분을 정의하려면 AndroidX 테스트 라이브러리의 일부인 ActivityScenario 인스턴스를 사용하세요. 이 클래스를 사용하면 기기 수준 이벤트를 시뮬레이션하는 상태에 활동을 배치할 수 있습니다.

ActivityScenario는 로컬 단위 테스트 및 기기 내 통합 테스트에서 모두 똑같이 사용할 수 있는 크로스 플랫폼 API입니다. 실제 또는 가상 기기에서 ActivityScenario는 스레드 안전성을 제공하여 테스트의 계측 스레드와 테스트 중인 활동을 실행하는 스레드 간에 이벤트를 동기화합니다.

이 API는 테스트 중인 활동이 제거되거나 생성될 때 어떻게 동작하는지 평가하는 데 특히 적합합니다. 이 섹션에서는 이 API와 관련된 가장 일반적인 사용 사례를 제시합니다.

활동 생성

테스트에 사용되는 활동을 생성하려면 다음 스니펫에 나와 있는 코드를 추가합니다.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
       launchActivity<MyActivity>().use {
       }
    }
}

활동을 생성한 후 ActivityScenario는 활동을 RESUMED 상태로 전환합니다. 이 상태는 활동이 실행 중이며 사용자에게 표시됨을 나타냅니다. 이 상태에서는 Compose 테스트 API를 사용하여 활동의 컴포저블과 자유롭게 상호작용할 수 있습니다.

테스트가 완료되면 활동에서 close를 호출하는 것이 좋습니다. 이렇게 하면 연결된 리소스가 정리되고 테스트의 안정성이 개선됩니다. ActivityScenarioCloseable를 구현하므로 활동이 자동으로 종료되도록 use 확장 프로그램을 적용할 수 있습니다.

또는 createAndroidComposeRule를 사용하여 각 테스트 전에 Activity를 자동으로 실행하고, 해제를 처리하고, Compose UI 테스트 메서드와 기본 ActivityScenario에 모두 액세스할 수 있습니다. 다음 예는 규칙을 정의하고 규칙에서 시나리오 인스턴스를 얻는 방법을 보여줍니다.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @get:Rule
    val composeTestRule = createAndroidComposeRule<MyActivity>()

    @Test fun testEvent() {
        val scenario = composeTestRule.activityRule.scenario
    }
}

새로운 상태로 활동 변경

활동을 CREATED 또는 STARTED와 같은 다른 상태로 변경하려면 moveToState를 호출하세요. 이 작업은 활동이 또 다른 앱이나 시스템 작업에 의해 중단되기 때문에 각각 중지되거나 일시중지된 상황을 시뮬레이션합니다.

moveToState의 사용 예는 다음 코드 스니펫에 나와 있습니다.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        launchActivity<MyActivity>().use { scenario ->
            scenario.moveToState(State.CREATED)
        }
    }
}

현재 활동 상태 확인

테스트 중인 활동의 현재 상태를 확인하려면 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 필드 값을 가져와야 합니다. createAndroidComposeRule를 사용하면 다음 코드 스니펫과 같이 활동을 종료하는 UI 작업을 쉽게 트리거할 수 있습니다.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @get:Rule
    val composeTestRule = createAndroidComposeRule<MyActivity>()

    @Test fun testResult() {
        composeTestRule.onNodeWithTag("finish_button").performClick()

        val scenario = composeTestRule.activityRule.scenario
        val resultCode = scenario.result.resultCode
        val resultData = scenario.result.resultData
    }
}

활동에서 작업 트리거

ActivityScenario 내의 모든 메서드는 차단 호출이므로 API를 사용하려면 계측 스레드에서 메서드를 실행해야 합니다.

테스트 중인 활동에서 작업을 트리거하려면 Compose 테스트 API를 사용하여 컴포저블과 상호작용하세요.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @get:Rule
    val composeTestRule = createAndroidComposeRule<MyActivity>()

    @Test fun testEvent() {
        composeTestRule.onNodeWithText("Refresh").performClick()
    }
}

그러나 활동 자체에서 메서드를 호출해야 한다면 onActivity을 사용하여 안전하게 실행할 수 있습니다.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        launchActivity<MyActivity>().use { scenario ->
            scenario.onActivity { activity ->
              activity.handleSwipeToRefresh()
            }
        }
    }
}

추가 리소스

테스트에 관한 자세한 내용은 다음 추가 리소스를 참고하세요.

문서