[Compose] UI Test

Choi Sang Rokยท2022๋…„ 8์›” 25์ผ
1

compose

๋ชฉ๋ก ๋ณด๊ธฐ
1/5

๐Ÿ’ก๊ฐœ์š”


ํ˜‘์—…ํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋“ค ๋ผ๋ฆฌ ์„œ๋กœ์˜ ์ฝ”๋“œ๋ฅผ ๋‹ค ์‚ดํŽด๋ณผ ์ˆœ ์—†์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ๊ฐ์ž ๊ฐœ๋ฐœํ•œ UI์— ๋Œ€ํ•ด ํ•œ๋ˆˆ์— ์•Œ์•„๋ณด๋Š” ๊ฒƒ์€ ๋”์šฑ ํž˜๋“  ์ผ์ž…๋‹ˆ๋‹ค.

UI Test๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ ๋งŒ์œผ๋กœ๋„ ์ด๊ฒƒ์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์œ ์ถ”๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋˜๊ณ , ์ด๊ฒƒ์€ ์ผ์ข…์˜ ๋ฌธ์„œ ์—ญํ• ์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ๋•ํ‚ค(Duckie)๋Š” ๋…๋ฆฝ์ ์ธ ๋””์ž์ธ ์‹œ์Šคํ…œ(Quack-Quack)์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋•ํ‚ค์˜ ๊ฑฐ์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” ๋””์ž์ธ ์‹œ์Šคํ…œ์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ์ด๋ฃจ์–ด์งˆ ๊ฒƒ์ด๋ฏ€๋กœ ์ค‘์š”์„ฑ์€ ์ด๋ฃจ ๋งํ•  ์ˆ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ UI Test๋ฅผ ํ†ตํ•ด UI๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ๊ฒ€์ฆํ•˜๊ณ , ์•ˆ์ •์„ฑ์„ ๊ฒ€์ฆํ•˜๋Š” ๊ณผ์ •์„ ํ•„์ˆ˜๋กœ ๊ฑฐ์น˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
https://github.com/sungbinland/duckie-quack-quack



๐ŸŽ†Semantic


UI Tree์— ์˜๋ฏธ๋ก ์ ์ธ ์š”์†Œ๋ฅผ ๋ถ€์—ฌํ•œ ์ฒด๊ณ„ ๋ผ๊ณ  ํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

.View System์—์„œ๋Š”, ๊ฐ View๊ฐ€ ๊ณ„์ธต ๊ตฌ์กฐ ๋‚ด์— ์ •์˜๋˜๊ณ , ๊ด€๋ จ ์†์„ฑ์„ ๋ณด์œ ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ปดํฌ์ €๋ธ”๊ฐ™์€ ๊ฒฝ์šฐ ์‹œ๊ฐ์  ํ‘œํ˜„์— ์ค‘์ ์„ ๋‘๋Š” background๋ผ๋˜์ง€, text๋ผ๋˜์ง€ ์ƒ์œ„ ์ปจํ…Œ์ด๋„ˆ์˜ ์ •๋ ฌ์„ ํฌํ•จํ•œ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ฐฉ์ถœํ•ฉ๋‹ˆ๋‹ค.ย 

์ด๋Ÿฌํ•œ ์˜๋ฏธ์—์„œ Semantic์€ ๋ฐฉ์ถœ๋œ ์‹œ์ ์— ์ปดํฌ์ €๋ธ”์— ์˜๋ฏธ๋ฅผ ๋ถ€์—ฌํ•˜๊ณ  ์ฐพ๊ธฐ ์œ„ํ•œ ์‹œ์Šคํ…œ์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Compose์˜ UI ํ…Œ์ŠคํŠธ๋Š” Semantic์„ ์‚ฌ์šฉํ•˜์—ฌ UI ๊ณ„์ธต ๊ตฌ์กฐ์™€ ์ƒํ˜ธ์ž‘์šฉ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Semantic tree๋Š” UI ๊ณ„์ธต ๊ตฌ์กฐ์™€ ํ•จ๊ป˜ ์ƒ์„ฑ๋˜๊ณ  UI ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ํ˜•์„ฑํ•ฉ๋‹ˆ๋‹ค

๊ฐ๊ฐ์˜ UI์š”์†Œ์— ์‹œ๋งจํ‹ฑ์ด ์˜๋ฏธ๋ฅผ ๋ถ€์—ฌํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด ์˜๋ฏธ๋ผ๋Š” ๊ฒƒ์€ Test FrameWork์—์„œ ์ ‘๊ทผํ• ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์‹์„ ๋งํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, composeTestRule.*onNodeWithText(text) ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ Semantic Node์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์‹์„ ๋งํ•ฉ๋‹ˆ๋‹ค.*



๐Ÿ’โ€โ™€๏ธExample


junit4 ํ™˜๊ฒฝ์—์„œ ๊ฐ„๋‹จํ•œ ๋ฒ„ํŠผ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ํ…Œ์ŠคํŠธ์˜ ํ๋ฆ„์„ ์งš์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

ComposeTestRule

ComposeTestRule์€ component, Activity๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๋ฐฉ๋ฒ• ๋ฐ finder, actions, assertions ๋“ฑ์„ ์ œ๊ณตํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค.
ํŠน์ • ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ ์ง€์ •ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด createAndroidComposeRule<ACTIVITY_NAME>() ์„ ํ†ตํ•ด ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@get:Rule
val composeTestRule = createComposeRule()

SetContent

๋•ํ‚ค ๋””์ž์ธ ์‹œ์Šคํ…œ(Quack-Quack) ์—์„œ๋Š” ๊ฐ๊ฐ์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ…Œ์ŠคํŠธ ํ•ด์•ผํ•˜๋ฏ€๋กœ, createComposeRule()์„ ์ƒ์„ฑํ•˜์—ฌ ๋…ธ๋“œ๋ฅผ ์ œ๊ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Semantic Node๋ฅผ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด, Companion Object์— ์„ ์–ธํ•œ TEST_TAG๋ฅผ ์ง€์ •ํ•ด ์ค๋‹ˆ๋‹ค.

composeTestRule.setContent{
	  Button(onClick = { }) {
        Text(TEST_TAG)
    }
}

Finders


Semantic์€ UI ์š”์†Œ์— ์˜๋ฏธ๋ฅผ ๋ถ€์—ฌํ•œ๋‹ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค.

Finder๋Š”, ์ฃผ์–ด์ง„ ์–ด๋– ํ•œ ์กฐ๊ฑด์„ ํ†ตํ•ด ์˜๋ฏธ๊ฐ€ ๋ถ€์—ฌ๋œ ๋…ธ๋“œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ๋“ค์–ด, argument๋กœ ์ฃผ์–ด์ง„ Text๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” Semantic Node๋ฅผ ์ฐพ์œผ๋ ค๋ฉด
composeTestRule.onNodeWithText(TEST_TAG) ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” SemanticsNodeInteraction ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์ฐพ์€ ๋…ธ๋“œ์— ๋Œ€ํ•œ Interaction์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ˜น์€ onAllNodes ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ๋…ธ๋“œ๋ฅผ ์„ ํƒํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

Assertion

ํŠน์ • ์š”์†Œ๊ฐ€ ์žˆ๋Š”์ง€, ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚ฌ๋Š”์ง€ ๋“ฑ์— ๋Œ€ํ•œ ์ƒํƒœ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

composeTestRule.onNodeWithText(TEST_TAG).assertIsDisplayed()



์ด์ฒ˜๋Ÿผ Finder, Assertion, Action์„ ์ ์ ˆํžˆ ํ™œ์šฉํ•˜์—ฌ ์š”์†Œ๋ฅผ ์ฐพ์•„ ์†์„ฑ์„ ํ™•์ธํ•˜๊ณ  Interaction์„ ์‹คํ–‰ํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.


https://developer.android.com/jetpack/compose/semantics?hl=ko

https://developer.android.com/jetpack/compose/testing?hl=ko

https://joebirch.co/android/jetpack-compose-accessibility-semantics/

profile
android_developer

3๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2022๋…„ 8์›” 26์ผ

์—ฌ๊ธฐ ๋ง›์ง‘์ด๋‹ค.

1๊ฐœ์˜ ๋‹ต๊ธ€
comment-user-thumbnail
2022๋…„ 10์›” 5์ผ

์ข‹์€ ๊ธ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ใ…Žใ…Ž

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ