이번 포스팅은 App Fragment Test 에 대하여 알아보도록 하겠습니다.
Fragment는 앱 내에서 재사용 가능한 컨테이너 역할을 하며, 다양한 Activity와 레이아웃 구성에서 동일한 사용자 인터페이스 레이아웃을 표시할 수 있습니다. Fragment는 다양한 용도로 사용되기 때문에 일관적이고 효율적으로 리소스를 사용하는 환경을 제공하는지 검증하는 것이 중요합니다.
▶ Fragment의 외형은 대형 스크린 화면이나 기기에서 가로 모드를 지원하는 Fragment를 포함하여 모든 레이아웃 구성에서 일관적이어야 합니다.
▶ Fragment가 사용자에게 보일 경우에만 Fragment의 뷰 계층 구조를 생성하세요.
Fragment의 상태 변경
이 테스트를 쉽게 설정할 수 있도록 AndroidX는 Fragment를 생성하고 상태를 변경하기 위한 FragmentScenario
라이브러리를 제공합니다.
★ 참고: FragmentScenario
객체가 포함된 테스트 실행을 성공하기 위해 Test 계측 스레드에서 각 API 메서드를 실행하세요. Android 테스트에서 사용한 여러 가지 스레드에 대한 자세한 내용은 테스트의 스레드 이해를 참조하세요.
Test artifact 위치 구성
FragmentScenario
를 의도한 대로 사용하려면 다음 소스 코드와 같이 앱의 테스트 APK에서 Fragment test artifact를 app/build.gradle 안에 정의합니다.
dependencies {
// ...
debugImplementation 'androidx.fragment:fragment-testing:{{ fragment_version }}'
}
Fragment 만들기
FragmentScenario
에는 다음 유형의 Fragment를 실행하기 위한 메서드가 포함됩니다.
▶사용자 인터페이스 포함하는 그래픽 Fragment 입니다. 이 유형의 Fragment를 실행하려면 launchFragmentInContainer()
를 호출합니다. FragmentScenario
는 Fragment를 Activity의 루트 뷰 컨트롤러에 연결합니다. 그 외의 경우에는 Activity가 포함된 이 작업은 비어 있습니다.
▶ 여러 Activity에 포함된 정보를 단기적으로 실행하거나 저장하는 비 그래픽 Fragment 입니다. 이 유형의 Fragment를 실행하려면 launchFragment()
를 호출합니다. FragmentScenario
는 이 유형의 조각을 루트 보기가 없는 완전히 빈 활동에 연결합니다.
이런 Fragment 유형 중 하나를 실행한 이후 FragmentScenario
는 테스트 중인 Fragment를 RESUMED
상태로 변경합니다. 이 상태는 Fragment가 실행 중이라는 것을 나타냅니다. 그래픽 Fragment를 테스트할 때 사용자에게도 테스트가 보이므로 Espresso UI 테스트를 사용하여 UI 요소에 대한 정보를 평가할 수 있습니다.
다음 소스 코드는 각 유형의 Fragment를 실행하는 방법을 보여줍니다.
<그래픽 Fragment 예시>
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
// The "state" and "factory" arguments are optional.
val fragmentArgs = Bundle().apply {
putInt("selectedListItem", 0)
}
val factory = MyFragmentFactory()
val scenario = launchFragmentInContainer<MyFragment>(
fragmentArgs, factory)
onView(withId(R.id.text)).check(matches(withText("Hello World!")))
}
}
<비 그래픽 Fragment의 예시>
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
// The "state" and "factory" arguments are optional.
val fragmentArgs = Bundle().apply {
putInt("numElements", 0)
}
val factory = MyFragmentFactory()
val scenario = launchFragment<MyFragment>(fragmentArgs, factory)
}
}
Fragment 다시 생성
기기에 리소스가 부족할 경우 시스템이 Fragment가 포함된 Activity를 소멸할 수 있으며, 이때 사용자가 앱에 돌아왔을 때 앱이 해당 Fragment를 다시 생성해야 합니다. 이 상황을 시뮬레이션하려면 recreate()
를 호출하세요.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<MyFragment>()
scenario.recreate()
}
}
FragmentScenario
클래스가 테스트 중인 Fragment를 다시 생성하면 Fragment는 다시 생성하기 이전의 수명 주기 상태로 돌아옵니다.
Fragment를 새로운 상태로 변경
앱 UI 테스트에서는 대개 테스트 중인 Fragment를 실행하고 다시 생성하는 것만으로 충분합니다. 그러나 세밀한 유닛 테스트에서는 Fragment가 하나의 수명 주기에서 다른 수명 주기로 넘어갈 때 Fragment의 동작을 평가할 수 있습니다.
Fragment를 다른 수명 주기 상태로 변경하려면 moveToState()
를 호출하세요. 이 메서드는 CREATED
, STARTED
, RESUMED
, DESTROYED
의 상태를 인수로 지원합니다. 이 작업은 Fragment가 포함된 Activity가 다른 앱이나 시스템 작업에 중단되어 상태를 변경하는 상황을 시뮬레이션합니다.
★ 참고: Fragment를 DESTROYED
상태로 전환할 경우 해당 Fragment를 다른 상태로 변경할 수 없고 Fragment를 다른 Activity에 연결할 수도 없습니다.
moveToState()
의 사용 예시가 다음 소스 코드에서 나타납니다.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<MyFragment>()
scenario.moveToState(State.CREATED)
}
}
★ 주의: 테스트 중인 Fragment를 현재의 상태로 전환하려고 할 경우 FragmentScenario
는 이 요청을 예외가 아니라 ‑작업 불능으로 취급합니다. 특히, 이 API를 사용하면 Fragment를 DESTROYED
상태로 연속해서 여러 번 전환할 수 있습니다.
Fragment에서 작업 자르기
테스트 중인 Fragment에서 작업을 트리거하려면 Espresso 뷰 매처를 사용하여 뷰에서 요소와 상호작용하세요.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<MyFragment>()
onView(withId(R.id.refresh))
.perform(click())
}
}
Fragment 자체에서 메서드를 호출해야 할 경우 (예: 옵션 메뉴에서의 선택에 응답) FragmentAction
을 구현하면 안전하게 호출할 수 있습니다.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<MyFragment>()
scenario.onFragment(fragment ->
fragment.onOptionsItemSelected(clickedItem) {
// Update fragment's state based on selected item.
}
}
}
}
★ 참고: 테스트 클래스에서는 onFragment()
로 전달한 객체에 대한 참조를 그대로 두지 마세요. 이 참조는 시스템 리소스를 사용하고, 프레임워크가 콜백 메서드로 전달된 Fragment를 다시 생성했기 때문에 참조 자체가 최신이 아닐 수 있습니다.
Reference
1. https://developer.android.com/training/basics/fragments/testing