Upgrade to Pro — share decks privately, control downloads, hide ads and more …

時計仕掛けのCompose

mkeeda
January 31, 2025

 時計仕掛けのCompose

Shibuya.apk #51での発表資料です。
https://shibuya-apk.connpass.com/event/342351/

この発表をするにあたって作ったサンプルのテストコードはこちらです。
https://github.com/mkeeda/recompose-control-deep-dive

mkeeda

January 31, 2025
Tweet

More Decks by mkeeda

Other Decks in Programming

Transcript

  1. 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() }
  2. 手 5 Side e ff ects (LaunchedE ff ect, produceState)

    mainClock.autoAdvance = false mainClock.advanceTimeBy(3000) mainClock.advanceTimeByFrame()
  3. 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
  4. 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 } } }
  5. 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
  6. 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
  7. 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
  8. 11

  9. 16 MainTestClock Test ComposeTestRule Monotonic FrameClock state Composition waitForIdle() Recomposer

    Frame Recompose advanceTimeByFrame() Frame recompose MainTestClock.autoAdvance = false
  10. MonotonicFrameClock Frame withFrameNanos() Frame suspend onFrame 1Frame withFrameNanos() Frame 17

    interface MonotonicFrameClock : CoroutineContext.Element { suspend fun <R> withFrameNanos( onFrame: (frameTimeNanos: Long) -> R ): R }
  11. 20

  12. 21 MainTestClock Test ComposeTestRule Monotonic FrameClock state Composition waitForIdle() Recomposer

    Frame Recompose advanceTimeByFrame() Frame recompose MainTestClock.autoAdvance = true onNode onNodeWithText("Hello").assertDoesNotExist()