Share
Embed
時計仕掛けのCompose
Shibuya.apk #51での発表資料です。
https://shibuya-apk.connpass.com/event/342351/
この発表をするにあたって作ったサンプルのテストコードはこちらです。
https://github.com/mkeeda/recompose-control-deep-dive
mkeeda
January 31, 2025
Transcript
21 slides
-
田 一 ( ) 𝕏 : @mr_mkeeda Github: @mkeeda Android
Engineer at Cybozu, Inc 💣 2 -
Compose 4 @Test fun successfulTest() = runComposeUiTest { var flag
by mutableStateOf(false) setContent { if (flag) { Text("Hello") } else { Text("World") } } onNodeWithText("Hello").assertDoesNotExist() onNodeWithText("World").assertExists() flag = true onNodeWithText("Hello").assertExists() onNodeWithText("World").assertDoesNotExist() } -
手 5 Side e ff ects (LaunchedE ff ect, produceState)
mainClock.autoAdvance = false mainClock.advanceTimeBy(3000) mainClock.advanceTimeByFrame() -
6 var isRunning by mutableStateOf(false) var taskStarted = false var
taskFinished = false setContent { if (isRunning) { LaunchedEffect(Unit) { taskStarted = true delay(3000) taskFinished = true } } } mainClock.autoAdvance = false isRunning = true mainClock.advanceTimeBy(1500) // ͚ͩ࣌ؒΛਐΊΔ taskStarted shouldBe true // λεΫ։͍࢝ͯ͠Δ͚Ͳ taskFinished shouldBe false // ऴΘͬͯͳ͍ mainClock.advanceTimeBy(1500) // ྃ࣌ࠁ·ͰਐΊΔ taskFinished shouldBe true // λεΫऴΘ͍ͬͯΔ FAILED ❌ testStarted = false -
7 mainClock.autoAdvance = false isRunning = true waitForIdle() // ⭐
mainClock.advanceTimeByFrame() // ⭐ mainClock.advanceTimeBy(1500) // ͚ͩ࣌ؒΛਐΊΔ taskStarted shouldBe true // λεΫ։͍࢝ͯ͠Δ͚Ͳ taskFinished shouldBe false // ऴΘͬͯͳ͍ mainClock.advanceTimeBy(1500) // ྃ࣌ࠁ·ͰਐΊΔ taskFinished shouldBe true // λεΫऴΘ͍ͬͯΔ SUCCESS ✅ var isRunning by mutableStateOf(false) var taskStarted = false var taskFinished = false setContent { if (isRunning) { LaunchedEffect(Unit) { taskStarted = true delay(3000) taskFinished = true } } } -
8 mainClock.autoAdvance = false isRunning = true mainClock.advanceTimeByFrame() // ⭐
waitForIdle() // ⭐ mainClock.advanceTimeBy(1500) // ͚ͩ࣌ؒΛਐΊΔ taskStarted shouldBe true // λεΫ։͍࢝ͯ͠Δ͚Ͳ taskFinished shouldBe false // ऴΘͬͯͳ͍ mainClock.advanceTimeBy(1500) // ྃ࣌ࠁ·ͰਐΊΔ taskFinished shouldBe true // λεΫऴΘ͍ͬͯΔ var isRunning by mutableStateOf(false) var taskStarted = false var taskFinished = false setContent { if (isRunning) { LaunchedEffect(Unit) { taskStarted = true delay(3000) taskFinished = true } } } FAILED ❌ testStarted = false -
9 mainClock.autoAdvance = false isRunning = true mainClock.advanceTimeByFrame() // ⭐
mainClock.advanceTimeBy(1500) // ͚ͩ࣌ؒΛਐΊΔ taskStarted shouldBe true // λεΫ։͍࢝ͯ͠Δ͚Ͳ taskFinished shouldBe false // ऴΘͬͯͳ͍ mainClock.advanceTimeBy(1500) // ྃ࣌ࠁ·ͰਐΊΔ taskFinished shouldBe true // λεΫऴΘ͍ͬͯΔ var isRunning by mutableStateOf(false) var taskStarted = false var taskFinished = false setContent { if (isRunning) { LaunchedEffect(Unit) { taskStarted = true delay(3000) taskFinished = true } } } FAILED ❌ testStarted = false -
10 mainClock.autoAdvance = false isRunning = true waitForIdle() // ⭐
mainClock.advanceTimeBy(1500) // ͚ͩ࣌ؒΛਐΊΔ taskStarted shouldBe true // λεΫ։͍࢝ͯ͠Δ͚Ͳ taskFinished shouldBe false // ऴΘͬͯͳ͍ mainClock.advanceTimeBy(1500) // ྃ࣌ࠁ·ͰਐΊΔ taskFinished shouldBe true // λεΫऴΘ͍ͬͯΔ var isRunning by mutableStateOf(false) var taskStarted = false var taskFinished = false setContent { if (isRunning) { LaunchedEffect(Unit) { taskStarted = true delay(3000) taskFinished = true } } } FAILED ❌ testStarted = false -
15 MainTestClock Test ComposeTestRule Monotonic FrameClock state Composition waitForIdle() Recomposer
Frame Recompose MainTestClock.autoAdvance = false -
16 MainTestClock Test ComposeTestRule Monotonic FrameClock state Composition waitForIdle() Recomposer
Frame Recompose advanceTimeByFrame() Frame recompose MainTestClock.autoAdvance = false -
MonotonicFrameClock Frame withFrameNanos() Frame suspend onFrame 1Frame withFrameNanos() Frame 17
interface MonotonicFrameClock : CoroutineContext.Element { suspend fun <R> withFrameNanos( onFrame: (frameTimeNanos: Long) -> R ): R } -
MonotonicFrameClock AndroidUiFrameClock Android 用 TestMonotonicFrameClock Frame Coroutine delay kotlinx.coroutines.test.TestCoroutineScheduler Frame
18 Frame 1 Frame 2 Frame 2 delay( 16 mills) delay( 16 mills) advanceTimeBy( 32 mills) -
21 MainTestClock Test ComposeTestRule Monotonic FrameClock state Composition waitForIdle() Recomposer
Frame Recompose advanceTimeByFrame() Frame recompose MainTestClock.autoAdvance = true onNode onNodeWithText("Hello").assertDoesNotExist()