Add first compose test
This commit is contained in:
parent
d74534d96b
commit
4feb92d4ed
5 changed files with 125 additions and 8 deletions
|
|
@ -132,6 +132,9 @@ dependencies {
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-core:$testing_espresso_version"
|
androidTestImplementation "androidx.test.espresso:espresso-core:$testing_espresso_version"
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-intents:$testing_espresso_version"
|
androidTestImplementation "androidx.test.espresso:espresso-intents:$testing_espresso_version"
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-contrib:$testing_espresso_version"
|
androidTestImplementation "androidx.test.espresso:espresso-contrib:$testing_espresso_version"
|
||||||
|
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$androidx_compose"
|
||||||
|
testImplementation "androidx.compose.ui:ui-test-junit4:$androidx_compose"
|
||||||
|
// debugImplementation "androidx.compose.ui:ui-test-manifest:$androidx_compose"
|
||||||
androidTestImplementation project(':mockserver')
|
androidTestImplementation project(':mockserver')
|
||||||
androidTestImplementation "androidx.arch.core:core-testing:$testing_androidx_arch_core_version"
|
androidTestImplementation "androidx.arch.core:core-testing:$testing_androidx_arch_core_version"
|
||||||
androidTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:$testing_junit5_version"
|
androidTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:$testing_junit5_version"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
package org.fnives.test.showcase.ui
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario
|
||||||
|
import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
|
||||||
|
import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule
|
||||||
|
import org.fnives.test.showcase.ui.compose.ComposeActivity
|
||||||
|
import org.fnives.test.showcase.ui.compose.TestShowCaseApp
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.rules.RuleChain
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.koin.test.KoinTest
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class AuthComposeInstrumentedTest : KoinTest {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val composeTestRule = createAndroidComposeRule<ComposeActivity>()
|
||||||
|
|
||||||
|
// private lateinit var activityScenario: ActivityScenario<ComposeActivity>
|
||||||
|
|
||||||
|
private val mockServerScenarioSetupTestRule = MockServerScenarioSetupResetingTestRule()
|
||||||
|
private val mockServerScenarioSetup get() = mockServerScenarioSetupTestRule.mockServerScenarioSetup
|
||||||
|
private val mainDispatcherTestRule = MainDispatcherTestRule()
|
||||||
|
private lateinit var robot: ComposeLoginRobot
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val ruleOrder: RuleChain = RuleChain.outerRule(mockServerScenarioSetupTestRule)
|
||||||
|
.around(mainDispatcherTestRule)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
robot = ComposeLoginRobot(composeTestRule)
|
||||||
|
composeTestRule.setContent {
|
||||||
|
TestShowCaseApp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @After
|
||||||
|
// fun tearDown() {
|
||||||
|
// activityScenario.safeClose()
|
||||||
|
// }
|
||||||
|
|
||||||
|
/** GIVEN non empty password and username and successful response WHEN signIn THEN no error is shown and navigating to home */
|
||||||
|
@Test
|
||||||
|
fun properLoginResultsInNavigationToHome() {
|
||||||
|
mockServerScenarioSetup.setScenario(
|
||||||
|
AuthScenario.Success(password = "alma", username = "banan")
|
||||||
|
)
|
||||||
|
composeTestRule.waitForIdle()
|
||||||
|
robot
|
||||||
|
.setPassword("alma")
|
||||||
|
.setUsername("banan")
|
||||||
|
.assertPassword("alma")
|
||||||
|
.assertUsername("banan")
|
||||||
|
.clickOnLogin()
|
||||||
|
// .assertLoadingBeforeRequests()
|
||||||
|
|
||||||
|
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
|
||||||
|
// robot.assertNavigatedToHome()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package org.fnives.test.showcase.ui
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.assertTextEquals
|
||||||
|
import androidx.compose.ui.test.junit4.ComposeTestRule
|
||||||
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
|
import androidx.compose.ui.test.performClick
|
||||||
|
import androidx.compose.ui.test.performTextInput
|
||||||
|
import org.fnives.test.showcase.ui.compose.screen.auth.AuthScreenTag
|
||||||
|
|
||||||
|
class ComposeLoginRobot(
|
||||||
|
private val composeTestRule: ComposeTestRule,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun setUsername(username: String): ComposeLoginRobot = apply {
|
||||||
|
composeTestRule.onNodeWithTag(AuthScreenTag.UsernameInput).performTextInput(username)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPassword(password: String): ComposeLoginRobot = apply {
|
||||||
|
composeTestRule.onNodeWithTag(AuthScreenTag.PasswordInput).performTextInput(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assertPassword(password: String): ComposeLoginRobot = apply {
|
||||||
|
composeTestRule.onNodeWithTag(AuthScreenTag.PasswordInput).assertTextEquals(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assertUsername(username: String): ComposeLoginRobot = apply {
|
||||||
|
composeTestRule.onNodeWithTag(AuthScreenTag.UsernameInput).assertTextEquals(username)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clickOnLogin(): ComposeLoginRobot = apply {
|
||||||
|
composeTestRule.onNodeWithTag(AuthScreenTag.LoginButton).performClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import android.os.Bundle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
import com.google.accompanist.insets.ProvideWindowInsets
|
import com.google.accompanist.insets.ProvideWindowInsets
|
||||||
import org.fnives.test.showcase.ui.compose.screen.AppNavigation
|
import org.fnives.test.showcase.ui.compose.screen.AppNavigation
|
||||||
|
|
||||||
|
|
@ -12,11 +13,16 @@ class ComposeActivity : AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContent {
|
setContent {
|
||||||
ProvideWindowInsets {
|
TestShowCaseApp()
|
||||||
MaterialTheme {
|
}
|
||||||
AppNavigation()
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Composable
|
||||||
|
fun TestShowCaseApp() {
|
||||||
|
ProvideWindowInsets {
|
||||||
|
MaterialTheme {
|
||||||
|
AppNavigation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
|
import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
|
@ -69,7 +70,7 @@ private fun CredentialsFields(authScreenState: AuthScreenState, modifier: Modifi
|
||||||
placeholder = { Text(text = stringResource(id = R.string.username)) },
|
placeholder = { Text(text = stringResource(id = R.string.username)) },
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email, imeAction = ImeAction.Next),
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email, imeAction = ImeAction.Next),
|
||||||
onValueChange = { authScreenState.onUsernameChanged(it) },
|
onValueChange = { authScreenState.onUsernameChanged(it) },
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth().testTag(AuthScreenTag.UsernameInput)
|
||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = authScreenState.password,
|
value = authScreenState.password,
|
||||||
|
|
@ -85,6 +86,7 @@ private fun CredentialsFields(authScreenState: AuthScreenState, modifier: Modifi
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 16.dp)
|
.padding(top = 16.dp)
|
||||||
|
.testTag(AuthScreenTag.PasswordInput)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -112,7 +114,7 @@ private fun Snackbar(authScreenState: AuthScreenState, modifier: Modifier = Modi
|
||||||
@Composable
|
@Composable
|
||||||
private fun LoginButton(modifier: Modifier = Modifier, onClick: () -> Unit) {
|
private fun LoginButton(modifier: Modifier = Modifier, onClick: () -> Unit) {
|
||||||
Box(modifier) {
|
Box(modifier) {
|
||||||
Button(onClick = onClick, Modifier.fillMaxWidth()) {
|
Button(onClick = onClick, Modifier.fillMaxWidth().testTag(AuthScreenTag.LoginButton)) {
|
||||||
Text(text = "Login")
|
Text(text = "Login")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -132,4 +134,10 @@ private fun AuthScreenState.ErrorType.stringResId() = when (this) {
|
||||||
AuthScreenState.ErrorType.GENERAL_NETWORK_ERROR -> R.string.something_went_wrong
|
AuthScreenState.ErrorType.GENERAL_NETWORK_ERROR -> R.string.something_went_wrong
|
||||||
AuthScreenState.ErrorType.UNSUPPORTED_USERNAME -> R.string.username_is_invalid
|
AuthScreenState.ErrorType.UNSUPPORTED_USERNAME -> R.string.username_is_invalid
|
||||||
AuthScreenState.ErrorType.UNSUPPORTED_PASSWORD -> R.string.password_is_invalid
|
AuthScreenState.ErrorType.UNSUPPORTED_PASSWORD -> R.string.password_is_invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object AuthScreenTag {
|
||||||
|
const val UsernameInput = "AuthScreenTag.UsernameInput"
|
||||||
|
const val PasswordInput = "AuthScreenTag.PasswordInput"
|
||||||
|
const val LoginButton = "AuthScreenTag.LoginButton"
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue