Slide 1

Slide 1 text

GDG Korea Android | ֎੉ߡઁ౟ KotlinConf'23 Global in Songdo Coroutine Testing ߓ೙઱

Slide 2

Slide 2 text

KotlinConf’23 in Songdo పझ౟ ௏٘о ೙ਃೠ ੉ਬܳ ੉೧ೡ ࣻ ੓׮. ௏ܖ౯ ղࠗীࢲ ੌযաח ੌਸ ੉೧ೡ ࣻ ੓׮. ௏ܖ౯ పझ౟ APIܳ ࢎਊ೧ࢲ పझ౟ ௏٘ܳ ੘ࢿೡ ࣻ ੓׮. য়ט੄ ݾ੸

Slide 3

Slide 3 text

KotlinConf’23 in Songdo 1. ੢গী ҙೠ नࣘೠ ೖ٘ߔ 2. ѐߊ ઱ӝীࢲ ઑӝ ੢গ х૑ 3. ഥӈী न҃ ॶ ೙ਃ হ੉ ௏٘ܳ ୭੸ച ೡ ࣻ ੓ب۾ ೞח ؊ উ੹ೠ ௏٘ ܻಖఠ݂ 4. ӝࣿ੸ ޙઁܳ ୭ࣗച ೞח উ੿੸ੋ ѐߊ ࣘب పझ౟ ௏٘੄ ੢੼

Slide 4

Slide 4 text

KotlinConf’23 in Songdo ਬ׫ పझ౟ ੘ࢿ ҃೷੉ ੓׮? vs হ׮?

Slide 5

Slide 5 text

KotlinConf’23 in Songdo হযب.. GPT ࢶࢤש੉ݶ غ૑ ঋਸөਃ?

Slide 6

Slide 6 text

KotlinConf’23 in Songdo “ݣ౭झۨ٘ ࢚ടীࢲ ਊ۝ਸ ૑੿ೡ ࣻ ੓ח ௸ܳ ٜ݅য઻” How we test concurrent algorithms in Kotlin Coroutines

Slide 7

Slide 7 text

KotlinConf’23 in Songdo import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.atomic.AtomicInteger class ConcurrentBoundedQueue(private val capacity: Int) { private val queue = ConcurrentLinkedQueue() private val size = AtomicInteger() fun add(value: T): Boolean { if (size.get() == capacity) { return false } queue.offer(value) size.incrementAndGet() return true } fun poll(): T? { val value = queue.poll() ?: return null size.decrementAndGet() return value } fun size(): Int = size.get() } How we test concurrent algorithms in Kotlin Coroutines

Slide 8

Slide 8 text

KotlinConf’23 in Songdo import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.atomic.AtomicInteger class ConcurrentBoundedQueue(private val capacity: Int) { private val queue = ConcurrentLinkedQueue() private val size = AtomicInteger() fun add(value: T): Boolean { if (size.get() == capacity) { return false } queue.offer(value) size.incrementAndGet() return true } fun poll(): T? { val value = queue.poll() ?: return null size.decrementAndGet() return value } fun size(): Int = size.get() } Ӓۢ GPT ࢶࢤשਸ ޺Ҋ ࢎਊೡ ࣻ ੓ח ௏٘ ੌөਃ? How we test concurrent algorithms in Kotlin Coroutines

Slide 9

Slide 9 text

KotlinConf’23 in Songdo val q = BoundedQueue(2) q.add(1) : true q.add(2) : true q.add(3) : false q.poll() : 1 ݏח Ѿҗੋоਃ? ࣻز పझ౟ How we test concurrent algorithms in Kotlin Coroutines

Slide 10

Slide 10 text

KotlinConf’23 in Songdo val q = BoundedQueue(2) q.add(1) : true q.add(2) : true q.add(3) : false // ਊ۝ ୡҗ q.poll() : 1 // ୐ߣ૩ ೦ݾ ୶୹ ࣻز పझ౟ ݏח Ѿҗੋоਃ? How we test concurrent algorithms in Kotlin Coroutines

Slide 11

Slide 11 text

KotlinConf’23 in Songdo val q = BoundedQueue(2) Thread1 Thread2 q.add("a") : true q.add("b") : true q.poll() // "a" q.poll() : // "b" ?? ?? زदࢿ੄ ࣁ҅ 1 How we test concurrent algorithms in Kotlin Coroutines

Slide 12

Slide 12 text

KotlinConf’23 in Songdo val q = BoundedQueue(2) Thread1 Thread2 q.add("a") : true q.poll() // "a" q.add("b") : true q.poll() : // "b" زदࢿ੄ ࣁ҅ 1 How we test concurrent algorithms in Kotlin Coroutines

Slide 13

Slide 13 text

KotlinConf’23 in Songdo val q = BoundedQueue(2) Thread1 Thread2 q.add("a") : true q.add("b") : true q.poll() // “b" q.poll() : // “a" ?? ?? زदࢿ੄ ࣁ҅ 2 How we test concurrent algorithms in Kotlin Coroutines

Slide 14

Slide 14 text

KotlinConf’23 in Songdo val q = BoundedQueue(2) Thread1 Thread2 q.add("a") : true q.add("b") : true q.poll() : // “a" q.poll() // “b" زदࢿ੄ ࣁ҅ 2 How we test concurrent algorithms in Kotlin Coroutines

Slide 15

Slide 15 text

KotlinConf’23 in Songdo val q = BoundedQueue(2) ?? ?? زदࢿ੄ ࣁ҅ 3 Thread1 Thread2 q.add("a") : true q.add("b") : true q.poll() // “b" q.add(“c") : true q.poll() : // “a" How we test concurrent algorithms in Kotlin Coroutines

Slide 16

Slide 16 text

KotlinConf’23 in Songdo val q = BoundedQueue(2) زदࢿ੄ ࣁ҅ 3 Thread1 Thread2 q.add("b") : true q.add("a") : true q.poll() // “b" q.add(“c") : true q.poll() : // “a" How we test concurrent algorithms in Kotlin Coroutines

Slide 17

Slide 17 text

KotlinConf’23 in Songdo val q = BoundedQueue(2) Thread1 Thread2 q.add(“2") : true q.add(“6") : true q.add(“ - 8“) : true ૒੽ ج۰ ࠇद׮! ⁉ How we test concurrent algorithms in Kotlin Coroutines

Slide 18

Slide 18 text

KotlinConf’23 in Songdo Ѿҗ ٮۄоӝ Thread1 Thread2 q.add(“6") : true size.get() == 0 queue.offer(6 ) Thread1 Thread2 q.add(“2") : true q.add(“6") : true q.add(“ - 8“) : true ⁉ queue empty Size 0 fun add(value: T): Boolean { if (size.get() == capacity) { return false } queue.offer(value) size.incrementAndGet() return true } How we test concurrent algorithms in Kotlin Coroutines

Slide 19

Slide 19 text

KotlinConf’23 in Songdo Ѿҗ ٮۄоӝ Thread1 Thread2 q.add(“6") : true size.get() == 0 queue.offer(6 ) Thread1 Thread2 q.add(“2") : true q.add(“6") : true q.add(“ - 8“) : true ⁉ queue 6 Size 0 How we test concurrent algorithms in Kotlin Coroutines

Slide 20

Slide 20 text

KotlinConf’23 in Songdo Ѿҗ ٮۄоӝ Thread1 Thread2 q.add(“6") : true size.get() == 0 queue.offer(6 ) q.add(“2") : true Thread1 Thread2 q.add(“2") : true q.add(“6") : true q.add(“ - 8“) : true ⁉ queue 6, 2 Size 1 How we test concurrent algorithms in Kotlin Coroutines

Slide 21

Slide 21 text

KotlinConf’23 in Songdo Ѿҗ ٮۄоӝ Thread1 Thread2 q.add(“6") : true size.get() == 0 queue.offer(6 ) q.add(“2") : true q.add(“ - 8“) : true Thread1 Thread2 q.add(“2") : true q.add(“6") : true q.add(“ - 8“) : true ⁉ queue 6, 2, -8 Size 2 How we test concurrent algorithms in Kotlin Coroutines

Slide 22

Slide 22 text

KotlinConf’23 in Songdo Ѿҗ ٮۄоӝ Thread1 Thread2 q.add(“6") : true size.get() == 0 queue.offer(6 ) q.add(“2") : true q.add(“ - 8“) : true size.incrementAndGet() : 3 result: true Thread1 Thread2 q.add(“2") : true q.add(“6") : true q.add(“ - 8“) : true ⁉ queue 6, 2, -8 Size 3 How we test concurrent algorithms in Kotlin Coroutines

Slide 23

Slide 23 text

KotlinConf’23 in Songdo Ѿҗ ٮۄоӝ https://kotlinconf.com/talks/400205/ Lincheck : https://github.com/Kotlin/kotlinx-lincheck = Invalid execution results = Init part: [addLast(4): void] Parallel part: | pollFirst(): 4 | addFirst(-4): void | | | peekLast(): 4 [-,1] | --- values in "[..]" brackets indicate the number of completed operations in each of the parallel threads seen at the beginning of the current operation --- = The following interleaving leads to the error = Parallel part trace: | pollFirst() | | | pollFirst(): 4 at ConcurrentDequeTest.pollFirst(ConcurrentDequeTest.kt:39) | | | first(): Node@1 at ConcurrentLinkedDeque.pollFirst(ConcurrentLinkedDeque.java:915) | | | item.READ: null at ConcurrentLinkedDeque.pollFirst(ConcurrentLinkedDeque.java:917) | | | next.READ: Node@2 at ConcurrentLinkedDeque.pollFirst(ConcurrentLinkedDeque.java:925) | | | item.READ: 4 at ConcurrentLinkedDeque.pollFirst(ConcurrentLinkedDeque.java:917) | | | prev.READ: null at ConcurrentLinkedDeque.pollFirst(ConcurrentLinkedDeque.java:919) | | | switch | | | | addFirst(-4): void | | | peekLast(): 4 | | | thread is finished | | compareAndSet(Node@2,4,null): true at ConcurrentLinkedDeque.pollFirst(..920) | | | unlink(Node@2) at ConcurrentLinkedDeque.pollFirst(ConcurrentLinkedDeque.java:921) | | | result: 4 | | | thread is finished | | How we test concurrent algorithms in Kotlin Coroutines

Slide 24

Slide 24 text

KotlinConf’23 in Songdo ਬ׫ పझ౟ ௏٘ܳ যڌѱ द੘೧ঠ ೡ ૑ ݽܰѷ׮ݶ .. https://product.kyobobook.co.kr/detail/S000001805070

Slide 25

Slide 25 text

KotlinConf’23 in Songdo ਋ܻо పझ౟ ೧ঠ ೡ Ѫ 1. View Model 2. Usecase 3. Repository

Slide 26

Slide 26 text

KotlinConf’23 in Songdo ਋ܻо పझ౟ ೧ঠ ೡ Ѫ ੑ۱җ ୹۱ ӒܻҊ ৘৻

Slide 27

Slide 27 text

KotlinConf’23 in Songdo ਋ܻо పझ౟ ೧ঠ ೡ Ѫ ੑ۱җ ୹۱ ӒܻҊ ৘৻ ViewModelী ؀೧ࢲ పझ౟ ௏٘ܳ ੘ࢿೠ׮ݶ 1. Repository ژח Usecase ীࢲ ߉ח ੑ۱ ч਷? 2. ചݶী ಴दೡ ੿࢚ ୹۱ ч਷? 3. ৘৻ܳ ചݶী ಴द೧઱۰ݶ ֈӡ ч਷ ޖ঺ੋо? ੉੹ ۨ੉য ( API, DB ) ੄ чਸ न҃ ॶ ೙ਃ

Slide 28

Slide 28 text

KotlinConf’23 in Songdo పझ౟ ࢸ੿ ৘द Usecase ViewModel Repository

Slide 29

Slide 29 text

KotlinConf’23 in Songdo పझ౟ ࢸ੿ ৘द Usecase ViewModel Repository class UseCase( private val repository: Repository, . . . ) { suspend operator fun invoke(): List<. . .> = withContext(defaultDispatcher) { } }

Slide 30

Slide 30 text

KotlinConf’23 in Songdo పझ౟ ࢸ੿ ৘द Usecase ViewModel Repository class UseCase( private val repository: Repository, . . . ) { suspend operator fun invoke(): List<. . .> = withContext(defaultDispatcher) { } } API - Repository р੄ ௏٘ Repository పझ౟ ௏٘ীࢲ Ѩૐ о੿

Slide 31

Slide 31 text

KotlinConf’23 in Songdo పझ౟ ࢸ੿ ৘द Usecase ViewModel Repository class UseCase( private val repository: Repository, . . . ) { suspend operator fun invoke(): List<. . .> = withContext(defaultDispatcher) { } } API - Repository р੄ ௏٘ Repository పझ౟ ௏٘ীࢲ Ѩૐ о੿ Mocking оמ

Slide 32

Slide 32 text

KotlinConf’23 in Songdo పझ౟ ࢸ੿ ৘द Usecase ViewModel Repository class UseCase( private val repository: Repository, . . . ) { suspend operator fun invoke(): List<. . .> = withContext(defaultDispatcher) { // పझ౟ ؀࢚ ۽૒ } } API - Repository р੄ ௏٘ Repository పझ౟ ௏٘ীࢲ Ѩૐ о੿ Repository Ѿҗ ா੉झী ؀೧ Usecase ۽૒ чী ؀ೠ పझ౟ ௏٘ ੘ࢿ Mocking оמ

Slide 33

Slide 33 text

KotlinConf’23 in Songdo Mocking ਸ ߄ۄࠁח ҙ੼ Mockingਸ ࢎਊೞѢա ࢎਊೞ૑ ঋѢա ١਷ ਤীࢲ ঱әೠ ଼ਸ ଵҊ೧ࢲ ੗न੄ झఋੌী ݏѱ ଻ఖ೧઱ࣁਃ. B class A class A classо B classী ੄ઓ

Slide 34

Slide 34 text

KotlinConf’23 in Songdo Mocking ਸ ߄ۄࠁח ҙ੼ Mockingਸ ࢎਊೞѢա ࢎਊೞ૑ ঋѢա ١਷ ਤীࢲ ঱әೠ ଼ਸ ଵҊ೧ࢲ ੗न੄ झఋੌী ݏѱ ଻ఖ೧઱ࣁਃ. A ز੘ਸ ৻ࠗ ৔ೱҗ ܻ࠙ పझ౟ ؀࢚ ௿ېझী݅ ૘઺ A class Mocking

Slide 35

Slide 35 text

KotlinConf’23 in Songdo ٘٣য ௏ܖ౯ 1. suspending functions పझ౟ ߑߨ 2. The Coroutine test apis 3. ࢎਊ ৘ઁ 4. Main Dispatcher ׮ܖӝ Untangling Coroutine Testing Untangling Coroutine Testing (https://kotlinconf.com/talks/389145/)

Slide 36

Slide 36 text

KotlinConf’23 in Songdo Testing suspending functions suspend fun fetchData(): String { delay(1000L) return "Hello world" } @Test fun dataIsHelloWorld() { val data = fetchData() assertEquals("Hello world", data) } https://developer.android.com/kotlin/coroutines/test?hl=ko

Slide 37

Slide 37 text

KotlinConf’23 in Songdo Testing suspending functions suspend fun fetchData(): String { delay(1000L) return "Hello world" } @Test fun dataIsHelloWorld() { val data = fetchData() assertEquals("Hello world", data) } suspend ੉ӝ ٸޙী Compile Error https://developer.android.com/kotlin/coroutines/test?hl=ko

Slide 38

Slide 38 text

KotlinConf’23 in Songdo Testing suspending functions suspend fun fetchData(): String { delay(1000L) return "Hello world" } @Test fun dataIsHelloWorld() = runTest { val data = fetchData() assertEquals("Hello world", data) } suspend పझ౟ח “runTest” https://developer.android.com/kotlin/coroutines/test?hl=ko

Slide 39

Slide 39 text

KotlinConf’23 in Songdo Testing suspending functions suspend fun fetchData(): String { return "Hello world" } @Test fun dataIsHelloWorld() = runTest { val data = fetchData() assertEquals("Hello world", data) } delay(1000L) Delayח ੗زਵ۽ Ѥցڭ https://developer.android.com/kotlin/coroutines/test?hl=ko

Slide 40

Slide 40 text

KotlinConf’23 in Songdo Testing suspending functions suspend fun fetchData(): String = withContext(Dispatchers.IO) { delay(1000L) return "Hello world" } @Test fun dataIsHelloWorld() = runTest { val data = fetchData() assertEquals("Hello world", data) } https://developer.android.com/kotlin/coroutines/test?hl=ko

Slide 41

Slide 41 text

KotlinConf’23 in Songdo Testing suspending functions suspend fun fetchData(): String = withContext(Dispatchers.IO) { delay(1000L) return "Hello world" } Test Thread Dispatchers.IO fetchData() delay() assert() https://developer.android.com/kotlin/coroutines/test?hl=ko

Slide 42

Slide 42 text

KotlinConf’23 in Songdo Testing suspending functions suspend fun fetchData(): String = withContext(Dispatchers.IO) { delay(1000L) return "Hello world" } Test Thread Dispatchers.IO fetchData() delay() assert() ੸੺൤ పझ౟ غ૑ ঋ਺ https://developer.android.com/kotlin/coroutines/test?hl=ko

Slide 43

Slide 43 text

KotlinConf’23 in Songdo Testing suspending functions suspend fun fetchData(): String = withContext(Dispatchers.IO) { delay(1000L) return "Hello world" } Test Thread Dispatchers.IO fetchData() delay() assert() ੸੺ೠ TestDispatcher ࢶఖ https://developer.android.com/kotlin/coroutines/test?hl=ko

Slide 44

Slide 44 text

KotlinConf’23 in Songdo TestDispatchers TestCoroutineScheduler runTest TestScope TestDispatcher https://developer.android.com/kotlin/coroutines/test?hl=ko

Slide 45

Slide 45 text

KotlinConf’23 in Songdo TestDispatchers TestCoroutineScheduler TestScope TestDispatcher StandardTestDispatcher UnconfinedTestDispatcher runTest https://developer.android.com/kotlin/coroutines/test?hl=ko

Slide 46

Slide 46 text

KotlinConf’23 in Songdo StandardTestDispatcher UnconfinedTestDispatcher ࢜۽਍ ௏ܖ౯੉ ӝࠄ झா઴۞ ௸ী ୶о ӝࠄਵ۽ ࢎਊؽ അ੤ झۨ٘ীࢲ ࢜ ௏ܖ౯ ߄۽ द੘ ࢶఖ੸ਵ۽ ࢎਊ TestDispatchers

Slide 47

Slide 47 text

KotlinConf’23 in Songdo StandardTestDispatcher @Test fun directExample() = runTest { val repo = UserRepositoy() launch { repo.register("Alice") } launch { repo.register("Bob") } assertEquals( listOf("Alice", "Bob"), repo.getAllUsers() ) }

Slide 48

Slide 48 text

KotlinConf’23 in Songdo StandardTestDispatcher @Test fun directExample() = runTest { val repo = UserRepositoy() launch { repo.register("Alice") } launch { repo.register("Bob") } assertEquals( listOf("Alice", "Bob"), repo.getAllUsers() ) }

Slide 49

Slide 49 text

KotlinConf’23 in Songdo StandardTestDispatcher @Test fun directExample() = runTest { launch { repo.register("Alice") } launch { repo.register("Bob") } . . . } Test Thread UserRepo() val repo = UserRepositoy()

Slide 50

Slide 50 text

KotlinConf’23 in Songdo StandardTestDispatcher @Test fun directExample() = runTest { launch { repo.register("Alice") } launch { repo.register("Bob") } . . . } Test Thread UserRepo() val repo = UserRepositoy() reg(“Alice”) reg(“Bob”)

Slide 51

Slide 51 text

KotlinConf’23 in Songdo StandardTestDispatcher @Test fun directExample() = runTest { launch { repo.register("Alice") } launch { repo.register("Bob") } assertEquals( listOf("Alice", “Bob"), repo.getAllUsers()) } Test Thread UserRepo() val repo = UserRepositoy() reg(“Alice”) reg(“Bob”) assert() ݽٚ ࢜ ௏ܖ౯਷ పझ౟ ௏ܖ౯੉ ৮ܐػ റ(Ӓ۞ա runTestо ߈ജೞӝ ੹)ী݅ प೯ؾפ׮.

Slide 52

Slide 52 text

KotlinConf’23 in Songdo StandardTestDispatcher @Test fun directExample() = runTest { launch { repo.register("Alice") } launch { repo.register("Bob") } advanceUntilIdle() assertEquals( listOf("Alice", “Bob"), repo.getAllUsers()) } Test Thread UserRepo() val repo = UserRepositoy() assert() reg(“Alice”) reg(“Bob”)

Slide 53

Slide 53 text

KotlinConf’23 in Songdo StandardTestDispatcher advanceUntilIdle() ؀ӝৌী թ਷ ೦ݾ੉ হਸ ٸө૑ झா઴۞ীࢲ ׮ܲ ௏ܖ౯ਸ ݽف प೯. ؀ӝ ઺ੋ ௏ܖ౯੉ ݽف प೯غب۾ ೞח જ਷ ӝࠄ ࢶఖ. ؀ࠗ࠙੄ పझ౟ दաܻয়ীࢲ ੘ز advanceTimeBy() о࢚ दрਸ ૑੿ೞҊ Ӓ ੹ী प೯غب۾ ৘ডػ ௏ܖ౯ਸ प೯ runCurrent() അ੤ о࢚ दрী ৘ডػ ௏ܖ౯ਸ प೯ ؀ӝৌী ୶оػ ௏ܖ౯੉ प೯غب۾ పझ౟ ௏ܖ౯ਸ ࢤࢿೞח ߑߨ

Slide 54

Slide 54 text

KotlinConf’23 in Songdo UnconfinedTestDispatcher @Test fun directExample() = runTest(UnconfinedTestDispatcher()) { val repo = UserRepositoy() launch { repo.register("Alice") } launch { repo.register("Bob") } assertEquals( listOf("Alice", "Bob"), repo.getAllUsers() ) } UnconfinedTestDispatcherח ࢜ ௏ܖ౯ਸ ࡅܰѱ प೯ೞݴ ௏ܖ౯ਸ ࢎਊೠ рױೠ పझ౟ী ੸೤೤פ׮.

Slide 55

Slide 55 text

KotlinConf’23 in Songdo Test Thread UserRepo() assert() reg(“Alice”) reg(“Bob”) UnconfinedTestDispatcher @Test fun directExample() = runTest(UnconfinedTestDispatcher()) { val repo = UserRepositoy() launch { repo.register("Alice") } launch { repo.register("Bob") } assertEquals( listOf("Alice", “Bob"), repo.getAllUsers()) }

Slide 56

Slide 56 text

KotlinConf’23 in Songdo Main dispatcher class HomeViewModel : ViewModel() { private val _message = MutableStateFlow("") val message: StateFlow get() = message fun loadMessage() { viewModelScope.launch { _message.value = "Greetings!" } } } @Test fun testGreeting() = runTest { val viewModel = HomeViewModel() viewModel.loadMessage() assertEquals("Greetings!", viewModel.message.value) }

Slide 57

Slide 57 text

KotlinConf’23 in Songdo Main dispatcher java.lang.illegalStateException Module with the Main dispatcher had failed to initialze. (For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used) Why? class HomeViewModel : ViewModel() { private val _message = MutableStateFlow("") val message: StateFlow get() = message fun loadMessage() { viewModelScope.launch { _message.value = "Greetings!" } } } ۽ஸ ױਤ పझ౟ীࢲח Android UI झۨ٘ܳ ېೝೞח Main ٣झಁ୊ܳ ࢎਊೡ ࣻ হणפ׮. ੉۞ೠ పझ౟ח Android ӝӝо ইצ ۽ஸ JVMীࢲ प೯غӝ ٸޙੑפ׮. పझ౟ ઺ੋ ௏٘о ӝࠄ झۨ٘ܳ ଵઑೞݶ ױਤ పझ౟ ઺ী ৘৻о ߊࢤ೤פ׮.

Slide 58

Slide 58 text

KotlinConf’23 in Songdo Main dispatcher @Test fun testGreeting() = runTest { val testDispatcher = UnconfinedTestDispatcher(testScheduler) Dispatchers.setMain(testDispatcher) try { val viewModel = HomeViewModel() viewModel.loadMessage() assertEquals("Greetings!", viewModel.message.value) } finally { Dispatchers.resetMain() } } viewModelScope৬ э਷ ੌࠗ APIח ղࠗ੸ਵ۽ ೞ٘௏٬ػ Main ٣झಁ୊ܳ ࢎਊ೤פ׮.

Slide 59

Slide 59 text

KotlinConf’23 in Songdo Main dispatcher @Test fun testGreeting() = runTest { val testDispatcher = UnconfinedTestDispatcher(testScheduler) Dispatchers.setMain(testDispatcher) try { val viewModel = HomeViewModel() viewModel.loadMessage() assertEquals("Greetings!", viewModel.message.value) } finally { Dispatchers.resetMain() } } ݽٚ ҃਋ী Main ٣झಁ୊ܳ TestDispatcher۽ ߄Բ۰ݶ Dispatchers.setMain ߂ Dispatchers.resetMain ೣࣻܳ ࢎਊ೤פ׮.

Slide 60

Slide 60 text

KotlinConf’23 in Songdo MainDispatcherRule class MainDispatcherRule( val dispatcher: TestDispatcher = UnconfinedTestDispatcher(), ) : TestWatcher() { override fun testing(description: Description) { Dispatchers.setMain(testDispatcher) } override fun finished(description: Description) { Dispatchers.resetMain() } }

Slide 61

Slide 61 text

KotlinConf’23 in Songdo MainDispatcherRule class HomeViewModelTestUsingRule { @get:Rule val mainDispatcherRule = MainDispatcherRule() @Test fun testGreeting() = runTest { val viewModel = HomeViewModel() viewModel.loadMessage() assertEquals("Greetings!", viewModel.message.value) } }

Slide 62

Slide 62 text

KotlinConf’23 in Songdo ਃড ௏ܖ౯ పझ౟ ೞ۰ݶ.. 1. runTest 2. Test Dispatchers 3. Main Dispatcher Resources • goo.gle/coroutine-test-guide • goo.gle/flow-test-guide

Slide 63

Slide 63 text

хࢎ೤פ׮! GDG Korea Android | ֎੉ߡઁ౟ KotlinConf'23 Global in Songdo veronikapj