프래그먼트는 앱에서 재사용할 수 있는 컨테이너 역할을 하므로 프래그먼트를 사용하면 다양한 활동과 레이아웃 구성에서 동일한 사용자 인터페이스 레이아웃을 제공할 수 있습니다. 이렇게 용도가 다양한 프래그먼트를 고려할 때 일관되고 리소스 효율적인 환경을 제공하는지 검증하는 것이 중요합니다.
- 프래그먼트의 모양은 대형 화면 크기나 가로 모드 기기 방향을 지원하는 레이아웃 구성을 포함하여 레이아웃 구성 전체에서 일관성이 있어야 합니다.
- 프래그먼트가 사용자에게 표시되지 않는 한 프래그먼트의 뷰 계층 구조를 만들지 마세요.
이 문서에서는 각 프래그먼트의 동작을 평가하는 테스트에 프레임워크 제공 API를 포함하는 방법을 설명합니다.
프래그먼트 상태 변경
이러한 테스트를 실행하는 조건을 설정하는 데 도움이 되도록 AndroidX는 프래그먼트를 만들고 그 상태를 변경하기 위한 FragmentScenario
라이브러리를 제공합니다.
종속 항목 선언
FragmentScenario
를 의도한 대로 사용하려면 다음 코드 스니펫과 같이 앱의 테스트 APK에서 프래그먼트 테스트 아티팩트를 정의합니다.
app/build.gradle
dependencies { def fragment_version = "1.2.4" // ... debugImplementation 'androidx.fragment:fragment-testing:$fragment_version' }
이 라이브러리의 현재 버전을 보려면 버전 페이지에서 프래그먼트에 관한 정보를 확인하세요.
프래그먼트 만들기
FragmentScenario
에는 다음 유형의 프래그먼트를 실행하는 메서드가 포함되어 있습니다.
이 메서드는 다음 유형의 프래그먼트도 지원합니다.
- 그래픽 프래그먼트. 사용자 인터페이스가 포함되어 있습니다. 이 유형의 프래그먼트를 실행하려면
launchFragmentInContainer()
를 호출합니다.FragmentScenario
는 프래그먼트를 활동의 루트 뷰 컨트롤러에 연결합니다. 이러한 활동은 그 외의 경우에는 비어 있습니다. - 그래픽이 아닌 프래그먼트(헤드리스 프래그먼트라고도 함). 여러 활동에 포함된 정보의 단기 처리를 저장하거나 실행합니다. 이 유형의 프래그먼트를 실행하려면
launchFragment()
를 호출합니다.FragmentScenario
는 이 유형의 프래그먼트를 완전히 비어 있는 활동(루트 뷰가 없는 활동)에 연결합니다.
이러한 프래그먼트 유형 중 하나의 실행을 시작한 후에는 FragmentScenario
가 테스트 중인 프래그먼트를 RESUMED
상태로 변경합니다. 이 상태는 프래그먼트가 실행 중이라는 것을 나타냅니다. 그래픽 프래그먼트를 테스트하고 있으면 사용자에게도 표시되므로 Espresso UI 테스트를 사용하여 UI 요소에 관한 정보를 평가할 수 있습니다.
다음 코드 스니펫은 각 유형의 프래그먼트를 실행하는 방법을 보여줍니다.
그래픽 프래그먼트 예
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEventFragment() { // The "fragmentArgs" 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!"))) } }
그래픽이 아닌 프래그먼트 예
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEventFragment() { // The "fragmentArgs" and "factory" arguments are optional. val fragmentArgs = Bundle().apply { putInt("numElements", 0) } val factory = MyFragmentFactory() val scenario = launchFragment<MyFragment>(fragmentArgs, factory) } }
프래그먼트 다시 만들기
기기의 리소스가 부족하면 시스템에서 프래그먼트가 포함된 활동을 제거할 수 있습니다. 따라서 사용자가 앱으로 돌아올 때 앱이 프래그먼트를 다시 만들어야 할 수 있습니다. 이러한 상황을 시뮬레이션하려면 recreate()
를 호출합니다.
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEventFragment() { val scenario = launchFragmentInContainer<MyFragment>() scenario.recreate() } }
FragmentScenario
클래스가 테스트 중인 프래그먼트를 다시 만들면 프래그먼트는 다시 만들어지기 전 수명 주기 상태로 돌아갑니다.
프래그먼트를 새로운 상태로 변경
앱의 UI 테스트에서는 테스트 중인 프래그먼트를 실행하고 다시 만드는 것만으로 충분한 경우가 많습니다. 그러나 세분화된 단위 테스트에서는 프래그먼트가 한 수명 주기 상태에서 다른 수명 주기 상태로 전환될 때 프래그먼트의 동작을 평가할 수도 있습니다.
프래그먼트를 다른 수명 주기 상태로 변경하려면 moveToState()
를 호출하세요.
이 메서드는 CREATED
, STARTED
, RESUMED
, DESTROYED
상태를 인수로 지원합니다. 이 작업에서는 프래그먼트가 포함된 활동이 다른 앱이나 시스템 작업으로 인해 중단되어 상태를 변경하는 상황을 시뮬레이션합니다.
moveToState()
의 사용 예는 다음 코드 스니펫에 나와 있습니다.
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEventFragment() { val scenario = launchFragmentInContainer<MyFragment>() scenario.moveToState(State.CREATED) } }
프래그먼트에서 작업 트리거
테스트 중인 프래그먼트에서 작업을 트리거하려면 Espresso 뷰 매처를 사용하여 뷰의 요소와 상호작용하세요.
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testEventFragment() { val scenario = launchFragmentInContainer<MyFragment>() onView(withId(R.id.refresh)) .perform(click()) } }
프래그먼트 자체에서 메서드를 호출해야 할 경우(예: 옵션 메뉴에서의 선택에 응답) 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. } } } }
대화상자 작업 테스트
FragmentScenario
는 대화상자 테스트도 지원합니다.
대화상자가 그래픽 프래그먼트의 인스턴스인 경우에도 launchFragment()
메서드를 사용하여 대화상자를 실행하는 활동이 아니라 대화상자 자체에 대화상자의 요소가 채워지도록 합니다.
다음 코드 스니펫은 대화상자 닫기 프로세스를 테스트합니다.
@RunWith(AndroidJUnit4::class) class MyTestSuite { @Test fun testDismissDialogFragment() { // Assumes that "MyDialogFragment" extends the DialogFragment class. with(launchFragment<MyDialogFragment>()) { onFragment { fragment -> assertThat(fragment.dialog).isNotNull() assertThat(fragment.requireDialog().isShowing).isTrue() fragment.dismiss() fragment.requireFragmentManager().executePendingTransactions() assertThat(fragment.dialog).isNull() } // Assumes that the dialog had a button // containing the text "Cancel". onView(withText("Cancel")).check(doesNotExist()) } } }