프래그먼트 테스트

이 주제에서는 각 프래그먼트의 동작을 평가하는 테스트에 프레임워크 제공 API를 포함하는 방법을 설명합니다.

프래그먼트는 앱에서 재사용할 수 있는 컨테이너 역할을 하므로 프래그먼트를 사용하면 다양한 활동과 레이아웃 구성에서 동일한 사용자 인터페이스 레이아웃을 제공할 수 있습니다. 용도가 다양한 프래그먼트를 고려하면 프래그먼트가 일관되고 리소스 효율적인 환경을 제공하는지 검증하는 것이 중요합니다. 다음 내용을 참고하세요.

  • 프래그먼트는 특정 상위 활동이나 프래그먼트에 종속되면 안 됩니다.
  • 프래그먼트가 사용자에게 표시되지 않는 한 프래그먼트의 뷰 계층 구조를 만들면 안 됩니다.

이러한 테스트 실행 조건을 설정하는 데 도움이 되도록 AndroidX fragment-testing 라이브러리에서는 프래그먼트를 만들고 그 Lifecycle.State를 변경하는 FragmentScenario 클래스를 제공합니다.

종속 항목 선언

FragmentScenario를 사용하려면 다음에서 fragment-testing-manifest 아티팩트를 정의합니다. debugImplementation를 사용하는 앱의 build.gradle 파일과 androidTestImplementation를 사용하는 fragment-testing 아티팩트 다음 예를 참고하세요.

Groovy

dependencies {
    def fragment_version = "1.8.3"

    debugImplementation "androidx.fragment:fragment-testing-manifest:$fragment_version"

    androidTestImplementation "androidx.fragment:fragment-testing:$fragment_version"
}

Kotlin

dependencies {
    val fragment_version = "1.8.3"

    debugImplementation("androidx.fragment:fragment-testing-manifest:$fragment_version")

    androidTestImplementation("androidx.fragment:fragment-testing:$fragment_version")
}

이 페이지의 테스트 예에서는 EspressoTruth 라이브러리의 어설션을 사용합니다. 사용 가능한 다른 테스트 및 어설션 라이브러리에 관한 자세한 내용은 AndroidX 테스트용 프로젝트 설정을 참고하세요.

프래그먼트 만들기

FragmentScenario에는 테스트에서 프래그먼트를 실행하는 다음 메서드가 포함되어 있습니다.

  • launchInContainer(): 프래그먼트의 사용자 인터페이스를 테스트합니다. FragmentScenario는 프래그먼트를 활동의 루트 뷰 컨트롤러에 연결합니다. 이러한 활동은 그 외의 경우에는 비어 있습니다.
  • launch(): 프래그먼트의 사용자 인터페이스 없이 테스트합니다. FragmentScenario는 이 유형의 프래그먼트를 루트 뷰가 없는 빈 활동에 연결합니다.

이러한 프래그먼트 유형 중 하나를 실행한 후에는 FragmentScenario가 테스트 중인 프래그먼트를 지정된 상태로 변경합니다. 기본적으로 이 상태는 RESUMED이지만 initialState 인수로 이를 재정의할 수 있습니다. RESUMED 상태는 프래그먼트가 실행 중이며 사용자에게 표시된다는 것을 나타냅니다. Espresso UI 테스트를 사용하여 UI 요소에 관한 정보를 평가할 수 있습니다.

다음 코드 예는 각 메서드를 사용하여 프래그먼트를 실행하는 방법을 보여 줍니다.

launchInContainer() 예

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        // The "fragmentArgs" argument is optional.
        val fragmentArgs = bundleOf(selectedListItem to 0)
        val scenario = launchFragmentInContainer<EventFragment>(fragmentArgs)
        ...
    }
}

launch() 예

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        // The "fragmentArgs" arguments are optional.
        val fragmentArgs = bundleOf("numElements" to 0)
        val scenario = launchFragment<EventFragment>(fragmentArgs)
        ...
    }
}

종속 항목 제공

프래그먼트에 종속 항목이 있다면 맞춤 FragmentFactorylaunchInContainer() 또는 launch() 메서드에 제공하여 이러한 종속 항목의 테스트 버전을 제공할 수 있습니다.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val someDependency = TestDependency()
        launchFragmentInContainer {
            EventFragment(someDependency)
        }
        ...
    }
}

FragmentFactory를 사용하여 프래그먼트에 종속 항목을 제공하는 방법에 관한 자세한 내용은 프래그먼트 관리자를 참고하세요.

프래그먼트를 새로운 상태로 변경

앱의 UI 테스트에서는 테스트 중인 프래그먼트를 실행하고 RESUMED 상태에서 테스트를 시작하는 것만으로 충분한 경우가 대부분입니다. 그러나 세분화된 단위 테스트에서는 프래그먼트가 한 수명 주기 상태에서 다른 수명 주기 상태로 전환될 때 프래그먼트의 동작을 평가할 수도 있습니다. initialState 인수를 모든 launchFragment*() 함수에 전달하여 초기 상태를 지정할 수 있습니다.

프래그먼트를 다른 수명 주기 상태로 변경하려면 moveToState()를 호출하세요. 이 메서드는 CREATED, STARTED, RESUMED, DESTROYED 상태를 인수로 지원합니다. 이 메서드는 프래그먼트가 포함된 프래그먼트나 활동이 어떤 이유로든 상태를 변경하는 상황을 시뮬레이션합니다.

다음 예에서는 INITIALIZED 상태에서 테스트 프래그먼트를 실행한 후 RESUMED 상태로 이동합니다.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>(
            initialState = Lifecycle.State.INITIALIZED
        )
        // EventFragment has gone through onAttach(), but not onCreate().
        // Verify the initial state.
        scenario.moveToState(Lifecycle.State.RESUMED)
        // EventFragment moves to CREATED -> STARTED -> RESUMED.
        ...
    }
}

프래그먼트 다시 만들기

앱이 리소스가 부족한 기기에서 실행 중이라면 시스템은 프래그먼트가 포함된 활동을 제거할 수도 있습니다. 이 상황에서는 사용자가 돌아올 때 앱이 프래그먼트를 다시 만들어야 합니다. 이 상황을 시뮬레이션하려면 recreate()를 호출하세요.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        scenario.recreate()
        ...
    }
}

FragmentScenario.recreate()는 프래그먼트와 그 호스트를 제거한 다음 다시 만듭니다. FragmentScenario 클래스가 테스트 중인 프래그먼트를 다시 만들면 프래그먼트는 제거되기 전 수명 주기 상태로 돌아갑니다.

UI 프래그먼트와 상호작용

테스트 중인 프래그먼트에서 UI 작업을 트리거하려면 Espresso 뷰 매처를 사용하여 뷰의 요소와 상호작용하세요.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        onView(withId(R.id.refresh)).perform(click())
        // Assert some expected behavior
        ...
    }
}

옵션 메뉴의 선택에 응답하는 것과 같이 프래그먼트 자체에서 메서드를 호출해야 한다면 FragmentScenario.onFragment()를 사용해 프래그먼트 참조를 가져오고 FragmentAction을 전달하여 안전하게 호출할 수 있습니다.

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        scenario.onFragment { fragment ->
            fragment.myInstanceMethod()
        }
    }
}

대화상자 작업 테스트

FragmentScenario대화상자 프래그먼트 테스트도 지원합니다. 대화상자 프래그먼트에는 UI 요소가 있지만 레이아웃은 활동 자체가 아닌 별도의 창에 채워집니다. 이러한 이유로 FragmentScenario.launch()를 사용하여 대화상자 프래그먼트를 테스트합니다.

다음 예는 대화상자 닫기 프로세스를 테스트합니다.

@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.parentFragmentManager.executePendingTransactions()
                assertThat(fragment.dialog).isNull()
            }
        }

        // Assumes that the dialog had a button
        // containing the text "Cancel".
        onView(withText("Cancel")).check(doesNotExist())
    }
}