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

Coroutines Test 入門 / Android Test Night #8

Coroutines Test 入門 / Android Test Night #8

star_zero

March 10, 2023
Tweet

More Decks by star_zero

Other Decks in Programming

Transcript

  1. Android Test Night #8
    2023/03/10
    Coroutines Test 入門

    View Slide

  2. ● Kenji Abe
    ● @STAR_ZERO
    ● Google Developers Expert for Android, Kotlin
    ● DeNA Co., Ltd.

    View Slide

  3. // build.gradle
    dependencies {
    // ...
    testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
    }

    View Slide

  4. supsend関数のテスト

    View Slide

  5. class Sample {
    suspend fun run(): String {
    return "hoge"
    }
    }

    View Slide

  6. class SampleTest {
    @Test
    fun testRun() = runTest {
    val sample = Sample()
    val result = sample.run()
    assert(result == "hoge")
    }
    }

    View Slide

  7. class SampleTest {
    @Test
    fun testRun() = runTest {
    val sample = Sample()
    val result = sample.run()
    assert(result == "hoge")
    }
    }

    View Slide

  8. ● runBlockingのようなもの
    ● delay関数はすべてスキップされる
    ○ Vertial Timeを制御することができる
    ● TestScopeが使用される
    runTest

    View Slide

  9. TestDispatchers

    View Slide

  10. ● StandardTestDispatcher
    ○ runTestデフォルト
    ○ TestCoroutineSchedulerを使用するシンプルなDispatcher
    ● UnconfinedTestDispatcher
    ○ Dispatchers.Unconfinedのようなもの
    ○ トップレベルのlaunch/asyncがすぐに実行される
    TestDispatchers

    View Slide

  11. class SampleTest {
    @Test
    fun testRun() = runTest {
    val sample = Sample()
    val result = sample.run()
    assert(result == "hoge")
    }
    }

    View Slide

  12. @Test
    fun test() = runTest {
    var result = 0
    launch {
    result = 1
    }
    assert(result == 1)
    }

    View Slide

  13. @Test
    fun test() = runTest {
    var result = 0
    launch {
    result = 1
    }
    assert(result == 1)
    }
    ❌ Failed

    View Slide

  14. @Test
    fun test() = runTest {
    var result = 0
    launch {
    result = 1
    }
    assert(result == 1)
    }



    View Slide

  15. @Test
    fun test() = runTest {
    var result = 0
    launch {
    result = 1
    }
    runCurrent()
    assert(result == 1)
    }
    ✅ Success

    View Slide

  16. @Test
    fun test() = runTest {
    var result = 0
    launch {
    result = 1
    }
    runCurrent()
    assert(result == 1)
    }




    View Slide

  17. @Test
    fun test() = runTest {
    var result = 0
    launch {
    delay(1000)
    result = 1
    }
    runCurrent()
    assert(result == 1)
    }

    View Slide

  18. @Test
    fun test() = runTest {
    var result = 0
    launch {
    delay(1000)
    result = 1
    }
    runCurrent()
    assert(result == 1)
    }
    ❌ Failed

    View Slide

  19. @Test
    fun test() = runTest {
    var result = 0
    launch {
    delay(1000)
    result = 1
    }
    runCurrent()
    assert(result == 1)
    }





    View Slide

  20. @Test
    fun test() = runTest {
    // ...
    advanceUntilIdle()
    // または
    advanceTimeBy(1001)
    assert(result == 1)
    }
    ✅ Success

    View Slide

  21. @Test
    fun test() = runTest(UnconfinedTestDispatcher()) {
    var result = 0
    launch {
    result = 1
    }
    assert(result == 1)
    }

    View Slide

  22. @Test
    fun test() = runTest(UnconfinedTestDispatcher()) {
    var result = 0
    launch {
    result = 1
    }
    assert(result == 1)
    }
    ✅ Success

    View Slide

  23. Mainスレッド対応

    View Slide

  24. class Sample {
    suspend fun run(): String =
    withContext(Dispatchers.Main) {
    "hoge"
    }
    }

    View Slide

  25. @Test
    fun testRun() = runTest {
    val sample = Sample()
    val result = sample.run()
    assert(result == "hoge")
    }

    View Slide

  26. @Test
    fun testRun() = runTest {
    val sample = Sample()
    val result = sample.run()
    assert(result == "hoge")
    }

    View Slide

  27. @Before
    fun setUp() {
    Dispatchers.setMain(StandardTestDispatcher())
    }
    @After
    fun tearDown() {
    Dispatchers.resetMain()
    }

    View Slide

  28. Flowのテスト

    View Slide

  29. class Sample {
    val flow = MutableSharedFlow()
    suspend fun emit(value: Int) {
    flow.emit(value)
    }
    }

    View Slide

  30. @Test
    fun test() = runTest(UnconfinedTestDispatcher()) {
    val sample = Sample()
    val result = mutableListOf()
    val job = launch {
    sample.flow.toCollection(result)
    }
    assert(result.size == 0)
    sample.emit(1)
    assert(result.size == 1)
    assert(result.first() == 1)
    job.cancel()
    }

    View Slide

  31. @Test
    fun test() = runTest(UnconfinedTestDispatcher()) {
    val sample = Sample()
    val result = mutableListOf()
    // ...
    }

    View Slide

  32. @Test
    fun test() = runTest(UnconfinedTestDispatcher()) {
    val sample = Sample()
    val result = mutableListOf()
    val job = launch {
    sample.flow.toCollection(result)
    }
    // ...
    }

    View Slide

  33. @Test
    fun test() = runTest(UnconfinedTestDispatcher()) {
    val sample = Sample()
    val result = mutableListOf()
    val job = launch {
    sample.flow.toCollection(result)
    }
    // ...
    }

    View Slide

  34. @Test
    fun test() = runTest(UnconfinedTestDispatcher()) {
    // ...
    val job = launch {
    sample.flow.toCollection(result)
    }
    assert(result.size == 0)
    // ...
    }

    View Slide

  35. @Test
    fun test() = runTest(UnconfinedTestDispatcher()) {
    // ...
    val job = launch {
    sample.flow.toCollection(result)
    }
    sample.emit(1)
    // ...
    }

    View Slide

  36. @Test
    fun test() = runTest(UnconfinedTestDispatcher()) {
    // ...
    sample.emit(1)
    assert(result.size == 1)
    assert(result.first() == 1)
    job.cancel()
    }

    View Slide

  37. @Test
    fun test() = runTest(UnconfinedTestDispatcher()) {
    // ...
    val job = launch {
    sample.flow.toCollection(result)
    }
    // ...
    job.cancel()
    }

    View Slide

  38. @Test
    fun test() = runTest(UnconfinedTestDispatcher()) {
    // ...
    backgroundScope.launch {
    sample.flow.toCollection(result)
    }
    // ...
    // job.cancel()
    }

    View Slide

  39. Turbine
    (時間の都合上省略...)
    https://github.com/cashapp/turbine

    View Slide

  40. まとめ

    View Slide

  41. ● runTest
    ● StandardTestDispatcher / UnconfinedTestDispatcher
    ● Dispatchers.setMain
    ● backgroundScope
    まとめ

    View Slide

  42. ありがとうございました

    View Slide