Skip to content

Commit 1a30df8

Browse files
Test the Game fragment via instrumented tests
1 parent 919ceb2 commit 1a30df8

File tree

6 files changed

+118
-1
lines changed

6 files changed

+118
-1
lines changed

app/build.gradle.kts

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ android {
3333
kotlinOptions {
3434
jvmTarget = "1.8"
3535
}
36+
packagingOptions {
37+
exclude("META-INF/AL2.0")
38+
exclude("META-INF/LGPL2.1")
39+
}
3640
}
3741

3842
dependencies {
@@ -74,4 +78,5 @@ dependencies {
7478
androidTestImplementation("androidx.arch.core:core-testing:_") // InstantTaskExecutorRule
7579
androidTestImplementation("androidx.test.espresso:espresso-core:_")
7680
androidTestImplementation("androidx.test.ext:junit-ktx:_") // activityScenarioRule
81+
androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:_") // runBlockingTest
7782
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package dev.gressier.pennydrop
2+
3+
import android.view.View
4+
import org.hamcrest.Description
5+
import org.hamcrest.TypeSafeMatcher
6+
7+
fun isActivated() = object : TypeSafeMatcher<View>() {
8+
9+
override fun describeTo(description: Description?) {
10+
description?.appendText("The View is activated")
11+
}
12+
13+
override fun matchesSafely(view: View?): Boolean =
14+
view?.isActivated == true
15+
}

app/src/androidTest/java/dev/gressier/pennydrop/TestHelpers.kt

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package dev.gressier.pennydrop
22

3+
import androidx.navigation.findNavController
4+
import androidx.test.core.app.ActivityScenario
5+
import androidx.test.espresso.Espresso.closeSoftKeyboard
36
import androidx.test.espresso.Espresso.onView
47
import androidx.test.espresso.action.ViewActions.click
58
import androidx.test.espresso.action.ViewActions.typeText
69
import androidx.test.espresso.matcher.ViewMatchers.withId
710
import androidx.test.espresso.matcher.ViewMatchers.withParent
11+
import kotlinx.coroutines.ExperimentalCoroutinesApi
12+
import kotlinx.coroutines.test.runBlockingTest
813
import org.hamcrest.Matchers.allOf
914

1015
fun typeInPlayerName(parentId: Int, text: String) {
@@ -23,4 +28,18 @@ fun clickPlayerCheckbox(parentId: Int) {
2328
withParent(withId(parentId))
2429
)
2530
).perform(click())
26-
}
31+
}
32+
33+
@ExperimentalCoroutinesApi
34+
fun startSampleGame(scenario: ActivityScenario<MainActivity>) =
35+
runBlockingTest {
36+
scenario.onActivity { activity ->
37+
activity.findNavController(R.id.containerFragment).navigate(R.id.pickPlayersFragment)
38+
}
39+
typeInPlayerName(R.id.mainPlayer, "Alex")
40+
typeInPlayerName(R.id.player2, "Sofiane")
41+
clickPlayerCheckbox(R.id.player3)
42+
typeInPlayerName(R.id.player3, "Adrien")
43+
closeSoftKeyboard()
44+
onView(withId(R.id.buttonPlayGame)).perform(click())
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package dev.gressier.pennydrop.fragments
2+
3+
import androidx.test.espresso.Espresso.onView
4+
import androidx.test.espresso.action.ViewActions.click
5+
import androidx.test.espresso.assertion.PositionAssertions.isCompletelyBelow
6+
import androidx.test.espresso.assertion.ViewAssertions.matches
7+
import androidx.test.espresso.matcher.ViewMatchers.*
8+
import androidx.test.ext.junit.rules.activityScenarioRule
9+
import dev.gressier.pennydrop.MainActivity
10+
import dev.gressier.pennydrop.R
11+
import dev.gressier.pennydrop.startSampleGame
12+
import kotlinx.coroutines.ExperimentalCoroutinesApi
13+
import kotlinx.coroutines.test.runBlockingTest
14+
import org.hamcrest.Matchers.*
15+
import org.junit.Assert.assertNotNull
16+
import org.junit.Assert.assertNull
17+
import org.junit.Before
18+
import org.junit.Rule
19+
import org.junit.Test
20+
import dev.gressier.pennydrop.isActivated as isLastRolled
21+
22+
@ExperimentalCoroutinesApi
23+
class GameFragmentTest {
24+
25+
@get:Rule var activityScenarioRule = activityScenarioRule<MainActivity>()
26+
27+
private val coinSlotMap = listOf(
28+
R.id.coinSlot1, R.id.coinSlot2, R.id.coinSlot3, R.id.coinSlot4, R.id.coinSlot5, R.id.coinSlot6,
29+
)
30+
.mapIndexed { i, it -> "${i + 1}" to it }
31+
.toMap()
32+
33+
@Before fun startNewGame() =
34+
runBlockingTest {
35+
startSampleGame(activityScenarioRule.scenario)
36+
}
37+
38+
@Test fun Check_starting_slots() {
39+
coinSlotMap.forEach { slotNumber, slotId ->
40+
onView(
41+
allOf(withId(R.id.slotNumberCoinSlot), withParent(withId(slotId)))
42+
).check(matches(withText(slotNumber)))
43+
44+
onView(
45+
allOf(withId(R.id.coinImageCoinSlot), withParent(withId(slotId)))
46+
).check(matches(not(isDisplayed())))
47+
48+
if (slotId != R.id.coinSlot6)
49+
onView(withId(R.id.coinSlot6)).check(isCompletelyBelow(withId(slotId)))
50+
}
51+
}
52+
53+
@Test fun Check_single_roll_result() =
54+
runBlockingTest {
55+
onView(withId(R.id.rollButton)).perform(click())
56+
57+
onView(withId(R.id.textCurrentPlayerName)).check(matches(withText("Alex")))
58+
onView(withId(R.id.textCurrentPlayerCoinsLeft)).check(matches(withText("9")))
59+
60+
onView(
61+
allOf(
62+
withId(R.id.bottomViewCoinSlot),
63+
isLastRolled(),
64+
anyOf(
65+
hasSibling(allOf(withId(R.id.coinImageCoinSlot), isDisplayed())),
66+
hasSibling(allOf(withId(R.id.slotNumberCoinSlot), withText("6"))),
67+
),
68+
)
69+
).check { view, noViewFoundException ->
70+
assertNull(noViewFoundException)
71+
assertNotNull(view)
72+
}
73+
}
74+
}

app/src/main/res/layout/fragment_game.xml

+2
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
app:layout_constraintEnd_toEndOf="parent">
125125

126126
<Button
127+
android:id="@+id/rollButton"
127128
android:layout_width="0dp"
128129
android:layout_height="wrap_content"
129130
android:layout_weight="3"
@@ -145,6 +146,7 @@
145146
android:textSize="24sp" />
146147

147148
<Button
149+
android:id="@+id/passButton"
148150
android:layout_width="0dp"
149151
android:layout_height="wrap_content"
150152
android:layout_weight="3"

versions.properties

+2
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,5 @@ version.kotlin=1.5.10
169169
## # available=1.5.30-M1
170170
## # available=1.5.30-RC
171171
## # available=1.5.30
172+
173+
version.kotlinx.coroutines=1.5.2

0 commit comments

Comments
 (0)