ãã®ã»ã¯ã·ã§ã³ã§ã¯ãJetpack Composeã«ãã£ãŠæ§ç¯ãããUIããã¹ãããæ¹æ³ãåŠã¶ã Jetpack Composeã§ã¯ãComposableé¢æ°ã«å¯ŸãããŠããããã¹ãã«ãã£ãŠãUIã«å¯Ÿãããã¹ããå®çŸã§ããã
- ãã¹ã察象ã®æŠèŠ
- Composeã®ãŠããããã¹ãã®æžãæ¹ãç解ãã
- å®è·µ: Composeã®ãŠããããã¹ããæžããŠã¿ã
- ãã¹ã察象ã¯ã©ã¹ïŒForYouScreen
åæç¶æ | ãã§ãã¯æžã¿ç¶æ | ãã£ãŒã |
---|---|---|
ForYouScreen
ã¯å
šäœçã«çžŠã¹ã¯ããŒã«å¯èœãšãªã£ãŠããã
ã¢ããªãèµ·åããçŽåŸã®ç»é¢ãåæç¶æ
ã®ã¹ã¯ãªãŒã³ã·ã§ããã§ããã
åæç¶æ
ã§ã¯è€æ°ã®ãããã¯(Headlines
, UI
, Compose
, ...)ã3è¡ãã€æšªæ¹åã«è¡šç€ºãããŠããã
ãã®ãããã¯äžèŠ§ã®éšåããªã³ããŒãã£ã³ã°ã»ã¯ã·ã§ã³ãšåŒã¶ã
ãªã³ããŒãã£ã³ã°ã»ã¯ã·ã§ã³ã®ç¹åŸŽã¯æ¬¡ã®ãšããã
- 暪ã¹ã¯ããŒã«å¯èœã§ãã
- ãããã¯ã®äžèŠ§ã衚瀺ãããŠãã
- äœããã§ãã¯ãããŠããªãå Žåã¯
Done
ãã¿ã³ãdisabledç¶æ ã«ãªã£ãŠãã - ãããã¯ã®åèŠçŽ ã¯ã¿ããå¯èœã§ãã¿ãããããšãã§ãã¯ãã€ã
ãªã³ããŒãã£ã³ã°ã»ã¯ã·ã§ã³å ã®ãããã¯ã«ãã§ãã¯ãä»ããç¶æ ãããã§ãã¯æžã¿ç¶æ ã®ã¹ã¯ãªãŒã³ã·ã§ããã§ããã ãã§ãã¯æžã¿ç¶æ ã®ç¹åŸŽã¯æ¬¡ã®ãšããã
- ãããã¯ã«ãã§ãã¯ãã€ããš
Done
ãã¿ã³ãenabledç¶æ ã«ãªã - ä»»æã®ãããã¯ããã§ãã¯ãããš
Done
ãã¿ã³ã®äžã«é¢é£ããèšäºã®äžèŠ§ã衚瀺ããã - ãããã¯ã¯è€æ°ãã§ãã¯ã§ãã
Done
ãã¿ã³ãã¿ãããããšãªã³ããŒãã£ã³ã°ã»ã¯ã·ã§ã³ãæ¶æ» ããèšäºã®äžèŠ§ã ãã®ç»é¢ã«ãªã
ãªã³ããŒãã£ã³ã°ã»ã¯ã·ã§ã³ã®ãããã¯ããã§ãã¯ãããšãDone
ãã¿ã³ã®äžã«é¢é£ããèšäºã®äžèŠ§ã衚瀺ãããã
ãã®èšäºã®äžèŠ§éšåããã£ãŒããšåŒã¶ã
ãŸããDone
ãã¿ã³ãæŒããšããªã³ããŒãã£ã³ã°ã»ã¯ã·ã§ã³ãæ¶æ»
ãããã£ãŒãã ãã®ç»é¢ã«ãªã(ãã£ãŒãã®ã¹ã¯ãªãŒã³ã·ã§ããåç
§)ã
ãã£ãŒãã®ç¹åŸŽã¯æ¬¡ã®ãšããã
- ãã©ããŒäžã®ãããã¯ããããããªã³ããŒãã£ã³ã°ã»ã¯ã·ã§ã³ã§ãããã¯ã«ãã§ãã¯ãå
¥ãããšã
Done
ãã¿ã³ã®äžã«è¡šç€ºããã - ããã¯ããŒã¯ãã¿ã³ãé¢é£ãããã¯(èšäºã®äžéšã«
HEADLINES
ãªã©ãšè¡šç€ºãããŠããéšå)ã¯ã¿ããå¯èœã§ãã - åèšäºã衚瀺ãããŠããã«ãŒãå šäœãã¿ããã§ãããã¿ãããããšãã®èšäºã®WebããŒãžã衚瀺ããã
- ãã¹ãã¯ã©ã¹ïŒForYouScreenTest
Composeã®ãŠããããã¹ãã§ã¯ViewModelã®çµåã¯è¡ããã空ã®Activityãèµ·åããŠãã¹ãããã å ¬åŒããã¥ã¡ã³ãã«ãããã£ãŠé©åã«ç¶æ ãã€ã¹ãã£ã³ã°ããªãããŠããComposableé¢æ°ã¯ãè²ã ãªç¶æ ãå€ããæž¡ããæ§é ã«ãªã£ãŠãããããã¹ã¿ããªãã£ãé«ãã
ãã ããç¶æ ãã€ã¹ãã£ã³ã°ãçšãããšãUIã®å€æŽã«ã€ãªããæ å ±ã¯ããã¹ãŠComposableé¢æ°ã®åŒæ°ãšããŠæž¡ãããUI StateããåŸãããšã«ãªãã UI Stateã®å€æŽã¯éåžžViewModelããéç¥ããããããViewModelãçµåããªãComposeã®ãŠããããã¹ãã§ã¯ãViewModelã«ããUI Stateã®å€æŽã«ãã£ãŠUIãå€åããããšããšãããã¹ãã¯æžããªãã 代ããã«ãåŒæ°ã«æž¡ãããUI Stateã®å 容ã«å¿ããŠãUIãæåŸ ã©ãã衚瀺ãããŠããããšã確èªãããã¹ããæžãã
Compose Testing APIããã¹ãã§äœ¿ããããã«ã次ã®äŸåã©ã€ãã©ãªãè¿œå ããã
dependencies {
androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version")
// createAndroidComposeRule()ã䜿ãå Žåã«å¿
èŠãšãªããcreateComposeRule()ãã䜿ããªãå Žåã¯äžèŠã
debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")
}
ããã¯Instrumented Testã§ãã¹ãããå Žåã®å®£èšã ããRobolectricã䜿ã£ãŠLocal Testã§ãã¹ãããããšãã§ããã
ãã®å Žåã¯androidTestImplementation
ãtestImplementation
ã«èªã¿æ¿ããããšã
Composeã®ãã¹ãã§ã¯createComposeRule
é¢æ°ãcreateAndroidComposeRule
é¢æ°åŒã³åºãã«ãã£ãŠååŸã§ããComposeTestRule
ãå©çšããã
ãã®ComposeTestRule
ãéããŠãããªãŒäžããç¹å®ã®ã³ã³ããŒãã³ããæ¢ããããããã«å¯Ÿããã¢ã¯ã·ã§ã³ãã¢ãµãŒã·ã§ã³ãå®è¡ã§ããã
ãã¹ããããComposableé¢æ°ããComposeTestRule
ã®setContent
å
ã§åŒã³åºãããšã§ããã¹ã察象ãèªç±ã«éžæã§ããã
class ForYouScreenTest {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@Test
fun my_first_compose_test() {
composeTestRule.setContent {
TODO("ããã§ãã¹ããããComposableé¢æ°ããåŒã³åºã")
}
}
}
Composeã®ãã¹ãã«äœ¿ãããAPIã«ã¯ã倧ãã4ã€ã«ããŽãªãŒã«åé¡ã§ããã
- FindersïŒç®çã®UIã³ã³ããŒãã³ãããComposeã®ã»ãã³ãã£ãã¯ããªãŒããç¹å®ããAPI
- AssertionsïŒ(Finders APIã«ãã£ãŠç¹å®ãã)UIã³ã³ããŒãã³ãã®å±æ§ãæåŸ ã©ããã§ããããšãæ€èšŒããAPI
- ActionsïŒ(Finders APIã«ãã£ãŠç¹å®ãã)UIã³ã³ããŒãã³ããæäœããAPI
- MatchersïŒããUIã³ã³ããŒãã³ããæºããã¹ãæ¡ä»¶(Matcher)ãè¿ãAPIã ãã®APIã«ãã£ãŠè¿ãããMatcherã¯ãFinders APIã®æ€çŽ¢æ¡ä»¶ããAssertions APIããæåŸ ã©ããã§ãããå€å®ããæ¡ä»¶ããšããŠå©çšããã
æ¬ãã³ãºãªã³ã§ã¯ãåã ã®APIã®è©³ãã説æã¯ããªãã Compose testing cheatsheet ãã«ããŽãªãŒå¥ã«Compose Testing APIãåé¡ããæ©èŠè¡šãšãªã£ãŠããã®ã§ããã®ããŒãã·ãŒããèŠãªãããã¹ããæžããšããã
Composeã®ãã¹ãã§ã¯ãæ§ç¯ãããã»ãã³ãã£ãã¯ããªãŒ(åŸè¿°)ããä»»æã®ã³ã³ããŒãã³ã(ã»ãã³ãã£ãã¯ããªãŒã®çšèªã§ã¯ããŒããšåŒã¶)ãèŠä»ããŠããã®ã³ã³ããŒãã³ãã«å¯ŸããŠã¢ãµãŒã·ã§ã³ãã¢ã¯ã·ã§ã³ãè¡ãã Finders APIã䜿ããšãç®çã®ããŒããã»ãã³ãã£ãã¯ããªãŒããèŠä»ããããšãã§ããã
contentDescription
ã«"Loading for youâŠ"
ãšèšå®ãããŠããUIã³ã³ããŒãã³ããæ¢ãã«ã¯æ¬¡ã®ããã«æžãã
composeTestRule.setContent {
ForYouScreen(...)
}
composeTestRule
.onNodeWithContentDescription("Loading for youâŠ")
代衚çãªFinders APIãããã€ã玹ä»ããã
onNodeWithText()
ãonNodeWithContentDescription()
ã®ä»ã«ãonNode()
ãšããã¡ãœãããååšããã
onNode()
ã¡ãœããã®åŒæ°ã«ä»»æã®Matcherãæž¡ãããšã§ãããªãŒããä»»æã®æ¡ä»¶ã«åèŽããããŒããæ¢ãããšãã§ããã
clickableãªããŒããã¿ã€ããã³ãŒãäŸã¯æ¬¡ã®ãšããã
composeTestRule
.onNode(hasClickAction())
è€æ°ã®MatcherãANDãORã§çµã¿åãããããšãã§ããã ãªãã§ããããã€ãclickableãªããŒããã¿ã€ããã³ãŒãäŸã¯æ¬¡ã®ãšããã
composeTestRule
.onNode(hasClickAction() and isOff())
onNode()
é¢æ°ã¯Matcherã«ããããã1ã€ã®ããŒã(æåã«ããããããã®)ãååŸã§ããã®ã«å¯ŸããŠãonAllNodes()
é¢æ°ã§ã¯Matcherã«ããããããã¹ãŠã®ããŒããååŸã§ããã
ããšãã°ãªã¹ããããããã®ãªã¹ãã«ã¯contentDescription
å±æ§ã"You can click this Item!"
ãšãªã£ãŠãããªã¹ãã¢ã€ãã ãè€æ°ãããšããã
ãã®ãããªãªã¹ãã¢ã€ãã ãã¹ãŠã«ã€ããŠãã¯ãªãã¯å¯èœã§ããããšãæ€èšŒããã³ãŒãã¯æ¬¡ã®ããã«ãªãã
composeTestRule
.onAllNodes(hasContentDescription("You can click this Item!"))
.assertAll(hasClickAction())
onNode()
é¢æ°ãªã©ã§ã¿ã€ããããŒãã®èŠªèŠçŽ ãåèŠçŽ ãèŠä»ããã«ã¯onParent()
ãonChild()
ãªã©ã䜿ãã
- 芪èŠçŽ ã«ã¢ã¯ã»ã¹ããäŸ
composeTestRule .onNodeWithText("test") .onParent()
- åèŠçŽ (è€æ°ã®åãããå Žåã¯å
é )ã«ã¢ã¯ã»ã¹ããäŸ
composeTestRule .onNodeWithText("test") .onChild() // åèŠçŽ ã®ããªãŒã®1çªå é
- ãã¹ãŠã®åèŠçŽ ã«ã¢ã¯ã»ã¹ããäŸ
composeTestRule .onNodeWithText("test") .onChildren() // ãã®å Žåã¯å šãŠã®åèŠçŽ
åã§è»œã觊ããããã«ãFinders APIãç®çã®UIã³ã³ããŒãã³ããæ¢ã察象ã¯ã»ãã³ãã£ãã¯ããªãŒã§ããã Jetpack Composeã§ã¯ãComposableé¢æ°ã®å®è¡ã«ãã£ãŠUIããªãŒãæ§ç¯ãããããããšäžç·ã«ã»ãã³ãã£ãã¯ããªãŒãæ§ç¯ããã
ã»ãã³ãã£ãã¯ããªãŒã«ã¯æç»ã«é¢ããæ å ±ã¯ãªãã 代ããã«ã³ã³ããŒã¶ãã«ã®æå³ã«é¢ããæ å ±ãå«ãŸããŠããããŠãŒã¶ãŒè£å©ãµãŒãã¹(ã¢ã¯ã»ã·ããªãã£)ãšãã¹ããã¬ãŒã ã¯ãŒã¯ããå©çšãããã
ãã®ããããŠãŒã¶ãŒè£å©ãµãŒãã¹ãšãã¹ããã¬ãŒã ã¯ãŒã¯ã®äž¡æ¹ããèªèãããããã»ãã³ãã£ãã¯ããªãŒã«ãªãããã«ãUIãæ§ç¯ããå¿ èŠãããã
Composeã®ãã¹ããæžããŠãããšãã©ãããŠãç®çã®ããŒããèŠä»ããããªãã£ãããããªãŒæ§é ãæ³å®ãšç°ãªãäºè±¡ã«æ©ãŸãããããšãããã
printToLog()
ã¡ãœããã䜿ããšã»ãã³ãã£ãã¯ããªãŒã®ãã°ãåºåã§ããã®ã§ããã®ãããªæã®ãããã°ã®æ段ãšããŠå©çšã§ããã
ãã°ããã©ããªããŒããã©ããªå€ãæã£ãŠããã®ããããªãŒæ§é ãæ³å®ã©ãããããªã©ã確èªã§ããã
composeTestRule.setContent {
ForYouScreen(...)
}
composeTestRule.onRoot().printToLog("Log") // printToLogã®åŒæ°ã«ã¯logcatã®ã¿ã°ãæå®ãã
Log : printToLog:
Log : Printing with useUnmergedTree = 'false'
Log : Node #1 at (l=0.0, t=299.0, r=1080.0, b=2208.0)px
Log : |-Node #4 at (l=0.0, t=299.0, r=1080.0, b=2208.0)px, Tag: 'forYou:feed'
Log : | VerticalScrollAxisRange = 'ScrollAxisRange(value=0.0, maxValue=0.0, reverseScrolling=false)'
Log : | CollectionInfo = 'androidx.compose.ui.semantics.CollectionInfo@f4fc348'
Log : | Actions = [IndexForKey, ScrollBy, ScrollToIndex]
Log : |-Node #7 at (l=458.0, t=299.0, r=623.0, b=464.0)px, Tag: 'forYou:loadingWheel'
Log : |-Node #8 at (l=480.0, t=321.0, r=601.0, b=442.0)px
Log : ContentDescription = '[Loading for youâŠ]'
ããšãã°ããã®ãã°ããã¯æ¬¡ã®ããšãåããã
Node #4
ã¯ã¹ã¯ããŒã«ã§ããNode #8
ã®contentDescription
å±æ§ã¯"Loading for youâŠ"
ã§ãã
Composeã®Modifier
ã«ã¯testTag()
ãšããã¡ãœãããååšããã
ãã®ã¡ãœããã䜿ããšã察å¿ããã»ãã³ãã£ãã¯ããªãŒã®ããŒãã«ãã¹ãã¿ã°ãä»ããããã
ãã¹ãã¿ã°ã«äžæãªæååãèšå®ãããšããã®ãã¹ãã¿ã°ãæ€çŽ¢æ¡ä»¶ã«ããããšã§ç®çã®ããŒããç°¡åã«èŠä»ããããããã«ãªãã
次ã®ã³ãŒãã§ã¯LazyVerticalGrid
ã«"forYou:feed"
ãšãããã¹ãã¿ã°ãä»äžããŠããã
LazyVerticalGrid(
columns = Adaptive(300.dp),
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalArrangement = Arrangement.spacedBy(24.dp),
modifier = modifier
.fillMaxSize()
.testTag("forYou:feed"),
state = state
)
ãã®ãšãã®ããªãŒæ§é ã¯æ¬¡ã®ããã«ãªãã
Node #4
ã«"forYou:feed"
ãšããTag
ãä»ããŠããããšãèªã¿åããã
Log : printToLog:
Log : Printing with useUnmergedTree = 'false'
Log : Node #1 at (l=0.0, t=299.0, r=1080.0, b=2208.0)px
Log : |-Node #4 at (l=0.0, t=299.0, r=1080.0, b=2208.0)px, Tag: 'forYou:feed'
Log : VerticalScrollAxisRange = 'ScrollAxisRange(value=0.0, maxValue=6.0, reverseScrolling=false)'
Log : CollectionInfo = 'androidx.compose.ui.semantics.CollectionInfo@e37367e'
Log : Actions = [IndexForKey, ScrollBy, ScrollToIndex]
Log : |-Node #6 at (l=44.0, t=343.0, r=1036.0, b=2456.0)px, Tag: 'news:expandedCard'
Log : Role = 'Button'
Log : Focused = 'false'
Log : Text = '[...]'
Log : HorizontalScrollAxisRange = 'ScrollAxisRange(value=0.0, maxValue=0.0, reverseScrolling=false)'
Log : Actions = [OnClick, RequestFocus, GetTextLayoutResult, ScrollBy]
Log : MergeDescendants = 'true'
Log : |-Node #17 at (l=871.0, t=926.0, r=981.0, b=1036.0)px
Log : | Role = 'Checkbox'
Log : | Focused = 'false'
Log : | ToggleableState = 'On'
Log : | ContentDescription = '[Unbookmark]'
Log : | Actions = [OnClick, RequestFocus]
Log : | MergeDescendants = 'true'
Log : |-Node #27 at (l=88.0, t=2291.0, r=248.0, b=2401.0)px
Log : Role = 'Button'
Log : Focused = 'false'
Log : ContentDescription = '[UI is followed]'
Log : Text = '[UI]'
Log : Actions = [OnClick, RequestFocus, GetTextLayoutResult]
Log : MergeDescendants = 'true'
ãã®ãã¹ãã¿ã°ãæ¡ä»¶ã«ããŠç®çã®ããŒããååŸãããã¹ãã³ãŒãã¯æ¬¡ã®ãšããã
composeTestRule.setContent {
ForYouScreen(...)
}
composeTestRule
.onNodeWithTag("forYou:feed")
ãã®ãã¹ãã³ãŒãã§ããã°ãç»é¢ãã¶ã€ã³å€æŽã«ãã£ãŠUIã®éå±€æ§é ãå€åããŠã(ã¿ã°ããå€æŽãããªããã°)ã³ãŒããä¿®æ£ããå¿ èŠããªãã
demoExerciseDebug
ãã«ãããªã¢ã³ãã§feature/foryou/src/androidTestExercise/java/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenTest.kt
ãéããŠäœæ¥ããdemoAnswerDebug
ãã«ãããªã¢ã³ãã«åãæ¿ãããšè§£çäŸã確èªã§ãã
Assertions APIã䜿ã£ãŠãUIã³ã³ããŒãã³ãã®ç¶æ ãæ€èšŒãããã¹ããæžããŠã¿ããã Finders APIã䜿ã£ãŠãã»ãã³ãã£ãã¯ããªãŒããç®çã®ããŒããèŠä»ãããããã ãã®ããŒãã«å¯ŸããŠAssertions APIãåŒã³åºããã
assertExists()
ã䜿ããšãç®çã®ããŒããã»ãã³ãã£ãã¯ããªãŒäžã«ååšããŠããããšã確èªã§ããã
@Test
fun `Loadingäžã«CircularProgressIndicatorãååšããŠããããš`() {
composeTestRule.setContent {
BoxWithConstraints {
ForYouScreen(
isSyncing = false,
onboardingUiState = OnboardingUiState.Loading,
feedState = NewsFeedUiState.Loading,
onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> }
)
}
}
composeTestRule
// Loading for youâŠãšããcontentDescriptionããã€ããŒããæ¢ã
.onNodeWithContentDescription("Loading for youâŠ")
// ããªãŒäžã«ååšããŠããããšã確èªãã
.assertExists()
}
UIã³ã³ããŒãã³ãã®enabledå±æ§ãæ€èšŒããã«ã¯assertIsEnabled()
ã»assertIsNotEnabled()
ã䜿ãã
@Test
fun `åæç¶æ
ã§ã¯Doneãã¿ã³ãdisableã«ãªã£ãŠããããš`() {
composeTestRule.setContent {
ForYouScreen(
isSyncing = false,
onboardingUiState = OnboardingUiState.Shown(topics = testTopics),
feedState = NewsFeedUiState.Success(emptyList()),
onTopicCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> }
)
}
composeTestRule
// DoneãšæžãããããŒããæ¢ã
.onNodeWithText("Done")
// disabledã§ãã(=enabledã§ã¯ãªã)ããšã確èªãã
.assertIsNotEnabled()
}
äžèšä»¥å€ã«ãããŸããŸãªAssertions APIãååšããã
assertIsDisplayed()
ã»assertIsNotDisplayed()
- ç»é¢äžã«è¡šç€ºãããŠãããã©ãããæ€èšŒãã
- Columnçã§æ§ç¯ãããUIã®å ŽåãããªãŒäžã«ã¯ååšãããç»é¢äžã«è¡šç€ºãããŠããªãå Žåãããã®ã§ãç»é¢äžã«è¡šç€ºãããŠããæ€èšŒããå Žåã«ã¯ãã¡ããå©çšãã
assertIsOn()
ã»assertIsOff()
- ãã§ãã¯ããã¯ã¹ã®ONã»OFFãæ€èšŒãã
assertHasClickAction
ã»assertHasNoClickAction
- ã¯ãªãã¯å¯èœãã©ãããæ€èšŒãã
ãã¹ãã¯ã©ã¹ForYouScreenTest
ã®æ¬¡ã®ãã¹ãã¡ãœããã«ã€ããŠã// TODO
éšåãåããŠãã¹ããå®æããããã
- ãã¹ãã¡ãœããïŒ
Headlinesãšæžããããããã¯ããã§ãã¯ãããŠããªãããš()
- ãã¹ãæŠèŠïŒãªã³ããŒãã£ã³ã°ã»ã¯ã·ã§ã³äžã®
Headlines
ãšæžããããããã¯ããã§ãã¯ãããŠããªãããšã確èªãã
ãã»ãã³ãã£ãã¯ããªãŒã®èŠ³å¯ããåèã«ããŸãã¯ããªãŒæ§é ãã©ããªã£ãŠããã®ã確èªããŠã¿ããã ããªãŒæ§é ãããã£ãããã¹ããæžããŠã¿ããã
Actions APIã䜿ã£ãŠãUIã³ã³ããŒãã³ãã®æäœãå®çŸã§ããã ViewModelãšçµåãããã¹ãã§ããã°ãæäœã®çµæUIã®ç¶æ ãå€åãããããåè¿°ã®Assertions APIãšçµã¿åãããã°å€åããUIã®ç¶æ ãæ€èšŒã§ããã
ãªããæ¬ã»ã¯ã·ã§ã³ã§æ±ã£ãŠããComposeã®ãŠããããã¹ãã¯ViewModelãšçµåããŠããªããããæäœã«ãã£ãŠUIãå€åããããšã¯ãªãã
代ããã«onClick
ã¢ã¯ã·ã§ã³ãªã©ãåŒã°ããããšæ€èšŒããããšã«ãªã(ç·Žç¿åé¡2)ã
performClick()
ã䜿ããšãç®çã®ããŒãã«å¯Ÿå¿ããã³ã³ããŒãã³ããã¯ãªãã¯ã§ããã
composeTestRule.setContent {
ForYouScreen(...)
}
composeTestRule
.onNodeWithText("Done")
.performClick()
ãã¹ãã¯ã©ã¹ForYouScreenTest
ã®æ¬¡ã®ãã¹ãã¡ãœããã«ã€ããŠã// TODO
éšåãåããŠãã¹ããå®æããããã
- ãã¹ãã¡ãœããïŒ
SingleTopicButtonãæŒããæã«onClickãåŒã°ããããš()
- ãã¹ãæŠèŠïŒæ¬¡ã®ããã«
SingleTopicButton
ãé 眮ãããŠããããããã¯ãªãã¯ãããšãã«onClick
ã¢ã¯ã·ã§ã³ãåŒã°ããããšã確èªããvar onClickCalled = false composeTestRule.setContent { BoxWithConstraints { SingleTopicButton( name = "UI", topicId = "TOPIC_ID_1", imageUrl = "", isSelected = false, onClick = { _, _ -> onClickCalled = true } ) } }
ãã®ã³ãŒãã§onClick
ã¢ã¯ã·ã§ã³ã«èšå®ãããŠããλåŒãåŒã°ãããšonClickCalled
ãtrue
ã«ãªãã
ãããå©çšããŠonClick
ã¢ã¯ã·ã§ã³ãåŒã°ããããšã確èªãããã
Composableãã¹ã¯ããŒã«å¯èœã§ããã€ã¹ã¯ããŒã«åŸã«è¡šç€ºãããUIã³ã³ããŒãã³ããæ€èšŒãããå Žåã¯ãperformScrollTo()
ãšããAPIãçšããã
performScrollTo()
ã¯ãç®çã®ããŒããç»é¢å
ã«çŸãããŸã§ã¹ã¯ããŒã«ããã
@Test
fun scrollTest() {
composeTestRule.setContent {
ScrollableScreen()
}
composeTestRule
.onNodeText("item1")
.performScrollTo()
}
// â»ãã®ã³ãŒãã¯ã¯Now In Androidã¢ããªã«ã¯ååšããªã
ãã®ã³ãŒãã¯ãitem1
ãšæžãããã³ã³ããŒãã³ãããããšãããŸã§ã¹ã¯ããŒã«ããã
ãã ããperformScrollTo()
ã¯ãã§ã«ã»ãã³ãã£ãã¯ããªãŒãæ§ç¯ãããŠãããã®ã®äžããããæ¢ãããšãã§ããªãã
ããªãã¡ãverticalScroll
ãhorizontalScroll
ãé©çšããŠããå Žåã«ããå©çšã§ããªãã
LazyColumn
ãLazyRow
ã䜿ã£ãŠãªã¹ããæ§ç¯ãããŠããå Žåã¯ããªã¹ãã¢ã€ãã ãç»é¢ã«è¡šç€ºãããçŽåã«UIãæ§ç¯ãããã
ãã®ããããªã¹ãã®äžã®æ¹ã«ããã³ã³ããŒãã³ããŸã§performScrollTo()
ã§ã¹ã¯ããŒã«ããããšããŠããç®çã®ããŒããèŠä»ããããšã©ãŒã«ãªã£ãŠããŸãã
ãã®ãããªã±ãŒã¹ã§ã¯ä»£ããã«performScrollToNode()
ãçšããã
performScrollToNode()
ã§ããã°ããŸã ã»ãã³ãã£ãã¯ããªãŒå
ã«ååšããŠããªãããŒãã§ãã£ãŠããããã«å¯Ÿå¿ããã³ã³ããŒãã³ãã衚瀺ããããŸã§ã¹ã¯ããŒã«ã§ããã
performScrollToNode()
ã¡ãœããã¯ãhasScrollToNodeAction()
ã«ããããã(ã¹ã¯ããŒã«å¯èœãª)ããŒãã«å¯ŸããŠåŒã³ã ãå¿
èŠãããã
ã¹ã¯ããŒã«ã«ãã£ãŠç»é¢å
ã«çŸããŠæ¬²ããããŒãã«å¯ŸããŠã§ã¯ãªãããšã«æ³šæããããšã
@Test
fun scrollToNodeTest() {
composeTestRule.setContent {
LazyScrollableScreen()
}
composeTestRule.onNode(hasScrollToNodeAction())
.performScrollToNode(hasText("item1")
}
// â»ãã®ã³ãŒãã¯ã¯Now In Androidã¢ããªã«ã¯ååšããªã
ãã¹ãã¯ã©ã¹ForYouScreenTest
ã®æ¬¡ã®ãã¹ãã¡ãœããã«ã€ããŠã// TODO
éšåãåããŠãã¹ããå®æããããã
- ãã¹ãã¡ãœããïŒ
ã¯ãªãã¯å¯èœãªãã£ãŒãã®1ã€ç®ãç»é¢äžã«è¡šç€ºãããŠããŠã2ã€ç®ãŸã§ã¹ã¯ããŒã«ã§ããããš()
- ãã¹ãæŠèŠïŒ1ã€ãã®ãã£ãŒããç»é¢äžã«è¡šç€ºãããŠããã2ã€ç®ã®ãã£ãŒããŸã§ã¹ã¯ããŒã«ã§ããããšã確èªãã
ãã»ãã³ãã£ãã¯ããªãŒã®èŠ³å¯ããåèã«ããŸãã¯ããªãŒæ§é ãã©ããªã£ãŠããã®ã確èªããŠã¿ããã
ã¯ãªãã¯å¯èœãªãã£ãŒãã®1ã€ç®ã«ã¯Android Basics with Compose
ãšããæååãã2ã€ç®ã«ã¯Thanks for helping us reach 1M YouTube Subscribers
ãšããæååãããããå«ãŸããŠããã
ãããèžãŸããŠã次ã®æé ããã¹ãã³ãŒãã«èœãšã蟌ããã
Android Basics with Compose
ãšããããã¹ããå«ãããŒããæ¢ããã¯ãªãã¯å¯èœã§ããããšãæ€èšŒãã
(ãã³ãïŒonNodeWithTextã®APIãªãã¡ã¬ã³ã¹ãèŠãŠãããã¹ãã®äžéšã«ããããããæ¹æ³ã確èªããã)performScrollToNode()
ã䜿ã£ãŠãThanks for helping us reach 1M YouTube Subscribers
ãšããããã¹ããå«ãããŒããç»é¢ã«è¡šç€ºããããŸã§ã¹ã¯ããŒã«ãã (ãã³ãïŒperformScrollTo()
ã䜿ã£ãã³ãŒããšã®éããæèããã)Thanks for helping us reach 1M YouTube Subscribers
ãšããããã¹ããå«ãããŒããç»é¢äžã«è¡šç€ºãããŠããããšãšãã¯ãªãã¯å¯èœã§ããããšãæ€èšŒãã
Jetpack Composeã«ãã£ãŠæ§ç¯ãããUIã«ã€ããŠãViewModelãå®Activityãšçµåããã«ãã¹ãããæ¹æ³ã説æããã
ãŸããComposeã®ãã¹ãã«äœ¿çšããCompose Testing APIã解説ããAssertions APIãActions APIã®äœ¿ãæ¹ãç·Žç¿åé¡ã解ããªããåŠãã ã ããã«çºå±çãªå 容ãšããŠãã¹ã¯ããŒã«ã絡ããã¹ãã®æ¹æ³ã玹ä»ããã