1
1
package co.nimblehq.sample.compose.ui.screens.main.home
2
2
3
3
import androidx.activity.compose.setContent
4
+ import androidx.compose.ui.platform.LocalContext
4
5
import androidx.compose.ui.test.assertIsDisplayed
5
6
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
6
7
import androidx.compose.ui.test.junit4.createAndroidComposeRule
8
+ import androidx.compose.ui.test.longClick
7
9
import androidx.compose.ui.test.onNodeWithText
8
10
import androidx.compose.ui.test.performClick
11
+ import androidx.compose.ui.test.performTouchInput
12
+ import androidx.navigation.compose.ComposeNavigator
13
+ import androidx.navigation.testing.TestNavHostController
9
14
import androidx.test.ext.junit.rules.ActivityScenarioRule
10
15
import androidx.test.rule.GrantPermissionRule
11
16
import co.nimblehq.sample.compose.domain.usecases.GetModelsUseCase
12
17
import co.nimblehq.sample.compose.domain.usecases.IsFirstTimeLaunchPreferencesUseCase
13
18
import co.nimblehq.sample.compose.domain.usecases.UpdateFirstTimeLaunchPreferencesUseCase
14
19
import co.nimblehq.sample.compose.test.MockUtil
15
20
import co.nimblehq.sample.compose.test.TestDispatchersProvider
16
- import co.nimblehq.sample.compose.ui.base.BaseDestination
21
+ import co.nimblehq.sample.compose.ui.AppNavGraph
17
22
import co.nimblehq.sample.compose.ui.screens.MainActivity
18
23
import co.nimblehq.sample.compose.ui.screens.main.MainDestination
19
24
import co.nimblehq.sample.compose.ui.theme.ComposeTheme
25
+ import dagger.hilt.android.testing.BindValue
26
+ import dagger.hilt.android.testing.HiltAndroidRule
27
+ import dagger.hilt.android.testing.HiltAndroidTest
20
28
import io.mockk.every
21
29
import io.mockk.mockk
22
30
import kotlinx.coroutines.flow.flowOf
23
- import org.junit.Assert.assertEquals
24
31
import org.junit.Before
25
32
import org.junit.Rule
26
33
import org.junit.Test
27
34
35
+ @HiltAndroidTest
28
36
class HomeScreenTest {
37
+ @get:Rule(order = 0 )
38
+ var hiltRule = HiltAndroidRule (this )
29
39
30
- @get:Rule
40
+ @get:Rule(order = 1 )
31
41
val composeRule = createAndroidComposeRule<MainActivity >()
42
+ private lateinit var navController: TestNavHostController
32
43
33
44
/* *
34
45
* More test samples with Runtime Permissions https://alexzh.com/ui-testing-of-android-runtime-permissions/
@@ -38,24 +49,27 @@ class HomeScreenTest {
38
49
android.Manifest .permission.CAMERA
39
50
)
40
51
41
- private val mockGetModelsUseCase: GetModelsUseCase = mockk()
42
- private val mockIsFirstTimeLaunchPreferencesUseCase: IsFirstTimeLaunchPreferencesUseCase = mockk()
43
- private val mockUpdateFirstTimeLaunchPreferencesUseCase: UpdateFirstTimeLaunchPreferencesUseCase = mockk()
52
+ private val mockGetModelsUseCase: GetModelsUseCase = mockk(relaxed = true )
53
+ private val mockIsFirstTimeLaunchPreferencesUseCase: IsFirstTimeLaunchPreferencesUseCase = mockk(relaxed = true )
54
+ private val mockUpdateFirstTimeLaunchPreferencesUseCase: UpdateFirstTimeLaunchPreferencesUseCase =
55
+ mockk(relaxed = true )
44
56
45
- private lateinit var viewModel: HomeViewModel
46
- private var expectedDestination: BaseDestination ? = null
57
+ // Cannot mock viewModel with mockk here because it will throw ClassCastException
58
+ // Ref: https://github.com/mockk/mockk/issues/321
59
+ @BindValue
60
+ val viewModel: HomeViewModel = HomeViewModel (
61
+ mockGetModelsUseCase,
62
+ mockIsFirstTimeLaunchPreferencesUseCase,
63
+ mockUpdateFirstTimeLaunchPreferencesUseCase,
64
+ TestDispatchersProvider
65
+ )
47
66
48
67
@Before
49
68
fun setUp () {
69
+ hiltRule.inject()
70
+
50
71
every { mockGetModelsUseCase() } returns flowOf(MockUtil .models)
51
72
every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flowOf(false )
52
-
53
- viewModel = HomeViewModel (
54
- mockGetModelsUseCase,
55
- mockIsFirstTimeLaunchPreferencesUseCase,
56
- mockUpdateFirstTimeLaunchPreferencesUseCase,
57
- TestDispatchersProvider
58
- )
59
73
}
60
74
61
75
@Test
@@ -71,23 +85,52 @@ class HomeScreenTest {
71
85
}
72
86
73
87
@Test
74
- fun when_clicking_on_a_list_item__it_navigates_to_Second_screen () = initComposable {
88
+ fun when_clicking_on_a_list_item__it_navigates_to_Second_screen () = initComposableNavigation {
75
89
onNodeWithText(" 1" ).performClick()
76
90
77
- assertEquals(expectedDestination, MainDestination .Second )
91
+ onNodeWithText(" Second" ).assertIsDisplayed()
92
+
93
+ navController.currentBackStackEntry?.destination?.hasRoute(MainDestination .Second .route, null )
94
+ }
95
+
96
+ @Test
97
+ fun when_long_clicking_on_a_list_item_and_click_edit__it_navigates_to_Third_screen () = initComposableNavigation {
98
+ onNodeWithText(" 1" ).performTouchInput { longClick() }
99
+
100
+ onNodeWithText(" Edit" ).performClick()
101
+
102
+ onNodeWithText(" Third" ).assertIsDisplayed()
103
+
104
+ navController.currentBackStackEntry?.destination?.hasRoute(MainDestination .Third .route, null )
78
105
}
79
106
80
107
private fun initComposable (
81
108
testBody : AndroidComposeTestRule <ActivityScenarioRule <MainActivity >, MainActivity >.() -> Unit
82
109
) {
83
110
composeRule.activity.setContent {
111
+ navController = TestNavHostController (LocalContext .current)
112
+ navController.navigatorProvider.addNavigator(ComposeNavigator ())
84
113
ComposeTheme {
85
114
HomeScreen (
86
115
viewModel = viewModel,
87
- navigator = { destination -> expectedDestination = destination }
116
+ onNavigateToSecondScreen = {},
117
+ onNavigateToThirdScreen = {},
88
118
)
89
119
}
90
120
}
91
121
testBody(composeRule)
92
122
}
123
+
124
+ private fun initComposableNavigation (
125
+ testBody : AndroidComposeTestRule <ActivityScenarioRule <MainActivity >, MainActivity >.() -> Unit
126
+ ) {
127
+ composeRule.activity.setContent {
128
+ navController = TestNavHostController (LocalContext .current)
129
+ navController.navigatorProvider.addNavigator(ComposeNavigator ())
130
+ ComposeTheme {
131
+ AppNavGraph (navController)
132
+ }
133
+ }
134
+ testBody(composeRule)
135
+ }
93
136
}
0 commit comments