Skip to content

Commit 12be601

Browse files
committed
feat: view model testing and main dispatcher rule
1 parent 8d31b51 commit 12be601

File tree

2 files changed

+122
-0
lines changed

2 files changed

+122
-0
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.patika.getir_lite
2+
3+
import com.patika.getir_lite.data.ProductRepository
4+
import com.patika.getir_lite.data.local.model.toProductWithCount
5+
import com.patika.getir_lite.fake.FakeProductDataSource
6+
import com.patika.getir_lite.model.BaseResponse
7+
import com.patika.getir_lite.model.ProductEvent
8+
import com.patika.getir_lite.util.MainDispatcherRule
9+
import kotlinx.coroutines.ExperimentalCoroutinesApi
10+
import kotlinx.coroutines.flow.first
11+
import kotlinx.coroutines.test.advanceTimeBy
12+
import kotlinx.coroutines.test.advanceUntilIdle
13+
import kotlinx.coroutines.test.runTest
14+
import org.junit.Assert.assertEquals
15+
import org.junit.Before
16+
import org.junit.Rule
17+
import org.junit.Test
18+
19+
@OptIn(ExperimentalCoroutinesApi::class)
20+
class ProductViewModelTest {
21+
22+
@get:Rule
23+
val dispatcherRule = MainDispatcherRule()
24+
25+
private val repository: ProductRepository = FakeProductDataSource()
26+
private lateinit var viewModel: ProductViewModel
27+
28+
@Before
29+
fun setup() {
30+
viewModel = ProductViewModel(repository)
31+
}
32+
33+
@Test
34+
fun `handle rapid consecutive product events correctly`() = runTest {
35+
val addCount = 100
36+
val minusCount = 49
37+
repeat(addCount) {
38+
viewModel.onEvent(ProductEvent.OnPlusClick(0L, it))
39+
advanceTimeBy(200) // mock the real delay
40+
}
41+
42+
repeat(minusCount) {
43+
viewModel.onEvent(ProductEvent.OnMinusClick(0L, it))
44+
advanceTimeBy(200)
45+
}
46+
47+
val expectedCount = addCount - minusCount
48+
val productResponse = viewModel.products.first()
49+
if (productResponse is BaseResponse.Success) {
50+
val pro = productResponse.data.find { it.productId == 0L }
51+
assertEquals(expectedCount, pro?.count)
52+
}
53+
}
54+
55+
@Test
56+
fun `handle high load of mixed operations gracefully`() = runTest {
57+
val products = repository.getProductsAsFlow().first()
58+
val suggestedProducts = repository.getSuggestedProductsAsFlow().first()
59+
60+
products.forEach {
61+
repeat(100) { _ ->
62+
viewModel.onEvent(ProductEvent.OnPlusClick(it.productId, 1))
63+
advanceTimeBy(200)
64+
viewModel.onEvent(ProductEvent.OnMinusClick(it.productId, 1))
65+
advanceTimeBy(200)
66+
}
67+
}
68+
69+
suggestedProducts.forEach {
70+
repeat(100) { _ ->
71+
viewModel.onEvent(ProductEvent.OnPlusClick(it.productId, 1))
72+
advanceTimeBy(200)
73+
viewModel.onEvent(ProductEvent.OnMinusClick(it.productId, 1))
74+
advanceTimeBy(200)
75+
}
76+
}
77+
78+
advanceUntilIdle()
79+
80+
val productResponse = viewModel.products.first()
81+
val suggestedProductResponse = viewModel.suggestedProducts.first()
82+
if (productResponse is BaseResponse.Success && suggestedProductResponse is BaseResponse.Success) {
83+
val pro = productResponse.data.find { it.productId == 0L }
84+
val suggestedPro = suggestedProductResponse.data.find { it.productId == 1L }
85+
assertEquals(0, pro?.count)
86+
assertEquals(0, suggestedPro?.count)
87+
}
88+
}
89+
90+
@Test
91+
fun `update in product count reflects in basket with products`() = runTest {
92+
val product = repository.getProductsAsFlow().first().first()
93+
viewModel.onEvent(ProductEvent.OnPlusClick(product.productId))
94+
advanceTimeBy(200)
95+
advanceUntilIdle()
96+
val basketResult = viewModel.basketWithProducts.first()
97+
if (basketResult is BaseResponse.Success) {
98+
val basket = basketResult.data
99+
val item = basket.itemsWithProducts.map { it.toProductWithCount() }
100+
.find { it.productId == product.productId }
101+
assertEquals(1, item?.count)
102+
}
103+
}
104+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.patika.getir_lite.util
2+
3+
import kotlinx.coroutines.Dispatchers
4+
import kotlinx.coroutines.ExperimentalCoroutinesApi
5+
import kotlinx.coroutines.test.TestDispatcher
6+
import kotlinx.coroutines.test.UnconfinedTestDispatcher
7+
import kotlinx.coroutines.test.resetMain
8+
import kotlinx.coroutines.test.setMain
9+
import org.junit.rules.TestWatcher
10+
import org.junit.runner.Description
11+
12+
@OptIn(ExperimentalCoroutinesApi::class)
13+
class MainDispatcherRule(
14+
private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
15+
) : TestWatcher() {
16+
override fun starting(description: Description?) = Dispatchers.setMain(testDispatcher)
17+
override fun finished(description: Description?) = Dispatchers.resetMain()
18+
}

0 commit comments

Comments
 (0)