Testing Codes/Testing Android

[Android UI Test] onNode와 Matcher 사용해 UI 노드 찾기

onNodeWithTag, onNodeWithText, onNodeWithContentDescription을 통해 UI 노드를 찾을 때의 한계점

onNodeWith- 구문을 사용해 UI 노드를 찾게 되면, 특정한 조건 하나만을 가진 UI 노드를 찾게 된다. 만약 같은 값을 공유하는 UI 노드가 있다면 둘 중 하나만이 선택되며, 그 둘을 구분할 수 있는 방법은 없다. 예를 들어 다음과 같이 같은 Smile이라는 이름을 가진 EmojiText Composable이 두 개 있다고 해보자.

class OnNodeTest {
    @get:Rule
    val composeRule = createComposeRule()

    @Test
    fun onNodeWithProblem() {
        // Given
        var isSecondSmileButtonClicked = false
        composeRule.setContent {
            Column() {
                EmojiText(
                    emoji = "😎",
                    content = "Smile"
                )
                EmojiText(
                    modifier = Modifier.clickable {
                        isSecondSmileButtonClicked = true
                    },
                    emoji = "😃",
                    content = "Smile"
                )
            }
        }

        // When
        composeRule.onNodeWithText("Smile").performClick()

        // ...
    }
}

 

만약 여기서 onNodeWithText를 통해 UI 노드를 찾는다면, 둘 중 하나(일반적으로 앞의 노드)가 찾아질 것이다. 만약 앞의 UI 노드가 선택됐다면, 뒤의 노드를 클릭하는 액션을 테스트하고 싶다면 방법이 없다. 

 

onNode를 통해 UI 노드를 정확하게 찾기

이를 해결하기 위해서는 ComposeContentTestRule 객체가 제공하는 onNode함수를 통해 UI 노드를 찾으면 된다. onNode 함수를 사용하면 더이상 특정한 값으로 UI 노드를 찾는 것이 아닌, SemanticsMatcher이라는 객체를 통해 해당 조건을 만족하는 UI 노드를 찾게돼 더욱 고도화된 UI 노드 찾기가 가능해진다.

fun onNode(
    matcher: SemanticsMatcher,
    useUnmergedTree: Boolean = false
): SemanticsNodeInteraction

 

SemanticsMatcher을 사용하는 방법은 간단하다. SemanticsMatcher 객체는 has- 구분을 통해 만들 수 있으며, 만약 둘 이상의 조건이 중복되어야 한다면 and 를 통해 둘 이상의 SemanticsMatcher 객체를 합칠 수 있다.

 

예를 들어 뒤의 EmojiText는 "Smile" 문자열 말고도, click 액션을 가지고 있으므로, hasText("Smile")과 hasClickAction()을 모두 만족하는 노드를 찾으면 된다. 따라서, 이 두가지를 and 를 사용해 연결하면 뒤의 EmojiText 노드를 찾을 수 있다.

 

class OnNodeTest {
    @get:Rule
    val composeRule = createComposeRule()

    @Test
    fun onNodeWithFixProblem() {
        // Given
        var isSecondSmileButtonClicked = false
        composeRule.setContent {
            Column() {
                EmojiText(
                    emoji = "😎",
                    content = "Smile"
                )
                EmojiText(
                    modifier = Modifier.clickable {
                        isSecondSmileButtonClicked = true
                    },
                    emoji = "😃",
                    content = "Smile"
                )
            }
        }

        // When
        composeRule
            .onNode(
                hasText("Smile")
                    .and(hasClickAction())
            )
            .performClick()

        // Then
        assertTrue(isSecondSmileButtonClicked)
    }
}

 

이제 이 테스트를 실행해보면 다음 화면과 같이 테스트가 통과되는 것을 볼 수 있다.

 

 

 

 

 

 

반응형

 

이 글의 저작권은 Dev World 에 있습니다. 글, 이미지 무단 재배포 및 변경을 금지합니다.

 

 

Kotlin, Android, Spring 사용자 오픈 카톡

오셔서 궁금한 점을 질문해보세요!
비밀번호 : kotlin22

open.kakao.com