Testing Codes/Testing Android

[Android UI Test] onAllNodes 구문 사용해 복수의 UI 노드 찾기

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

onNode- 구문을 사용해 UI 노드를 찾으면 한 번에 한 개의 노드 밖에 찾기 못한다. 예를 들어 다음과 컴포저블에 대해 Smile이라는 텍스트를 가진 모든 UI 노드를 찾아 클릭하는 테스트를 해야 한다고 해보자. 

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

    @Test
    fun onNodeAllNodes() {
        // Given
        val isClicked = Array(4) { false }
        composeRule.setContent {
            Column() {
                EmojiText(
                    modifier = Modifier.clickable {
                        isClicked[0] = true
                    },
                    emoji = "😁",
                    content = "Smile"
                )
                EmojiText(
                    modifier = Modifier.clickable {
                        isClicked[1] = true
                    },
                    emoji = "😆",
                    content = "Smile"
                )
                EmojiText(
                    modifier = Modifier.clickable {
                        isClicked[2] = true
                    },
                    emoji = "😎",
                    content = "Smile"
                )
                EmojiText(
                    modifier = Modifier.clickable {
                        isClicked[3] = true
                    },
                    emoji = "😃",
                    content = "Smile"
                )
            }
        }

        // When

        // Then
    }
}

 

이 컴포저블로 만들어진 UI의 모양은 다음과 같다.

그림1. 4개의 같은 텍스트를 가진 UI 노드들

 

만약 onNode- 구문을 사용해 찾아야 한다면, 다음과 같은 방식으로 각 노드를 찾아 클릭을 실행해야 한다.

// When
composeRule.onNodeWithText("😁").performClick()
composeRule.onNodeWithText("😆").performClick()
composeRule.onNodeWithText("😎").performClick()
composeRule.onNodeWithText("😃").performClick()

 

하지만, 이 방식은 다음과 같은 문제점이 있다.

  1. Smile이라는 텍스트를 가진 모든 노드를 찾아 클릭을 실행해야 하는데, 그와 관련 없는 이모지를 구분자로 사용해 UI 노드를 찾고 클릭 이벤트를 발생시켰다.
  2. 같은 코드가 여러 번 반복된다.

 

특히 1번의 경우 코드의 가독성 측면과 유지 보수성 측면에서 치명적이다. 다음 개발자가 왔을 때 이 테스트가 "Smile"을 가진 모든 Composable을 찾아 클릭하는 테스트라는 것을 코드만 보고 알기 어려울 것이다.

 

onAllNodes- 구문 사용해 특정 조건을 만족하는 모든 UI 노드 찾기

이렇게 특정 조건을 만족하는 모든 UI 노드를 찾기 위한 해답은 바로 onAllNodes- 구문을 사용하는 것이다. 하나의 노드를 찾

기 위해 onNode, onNodeWithTag, onNodeWithText, onNodeWithContentDescription 가 있는 것처럼, onAllNodes- 구문도 onAllNodes, onAllNodesWithTag, onAllNodesWithText, onAllNodesWithText 가 있으며, 사용 방법은 onNode- 구문과 같다. 다른 점은 onAllNodes- 구문은 복수의 UI 노드를 찾는다는 점이다.

 

그림2. onAllNodes- 구문

 

그러면 직전에 다룬 테스트의 요구 사항인 "Smile"이라는 텍스트를 가진 모든 노드를 찾아 클릭하는 UI 테스트를 onAllNodes- 구문을 사용해 작성해 보자. 특정 문자열을 가진 UI 노드를 찾아야 하므로 onAllNodesWithText를 사용한다. 그러면 테스트는 다음과 같아진다.

    @Test
    fun onNodeAllNodesClicked() {
        // Given
        ...
        
        // When
        val nodes = composeRule.onAllNodesWithText("Smile")
        for (index in 0 until nodes.fetchSemanticsNodes().size) {
            nodes[index].performClick()
        }

        // Then
        assertTrue(isClicked.all { clicked -> clicked })
    }
* 위 코드에서 forEach를 사용하지 않은 이유는, onAllNodesWith- 구문은 SemanticsNodeInteractionCollection 타입의 객체를 반환하는데, 이 객체에 대한 forEach 확장함수가 정의되어 있지 않기 때문에 위와 같이 for문을 통해 각 노드를 순환하는 방법을 채택했다.

 

 

이 테스트에서는 onAllNodesWithText("Smile")을 사용해 Smile이라는 텍스트를 가진 모든 UI 노드를 찾고, 해당 노드를 순환하며, 클릭을 발생시킨다.

 

이제 테스트를 실행시켜보면, 다음과 같은 결과가 나오는 것을 볼 수 있다.

 

 

모든 노드를 순환해 클릭 이벤트가 발생해 테스트가 통과된다.

반응형

 

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

 

 

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

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

open.kakao.com