Add first compose test

This commit is contained in:
Alex Gabor 2022-03-01 16:18:02 +02:00
parent d74534d96b
commit 4feb92d4ed
5 changed files with 125 additions and 8 deletions

View file

@ -132,6 +132,9 @@ dependencies {
androidTestImplementation "androidx.test.espresso:espresso-core:$testing_espresso_version"
androidTestImplementation "androidx.test.espresso:espresso-intents:$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 "androidx.arch.core:core-testing:$testing_androidx_arch_core_version"
androidTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:$testing_junit5_version"

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -4,6 +4,7 @@ import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import com.google.accompanist.insets.ProvideWindowInsets
import org.fnives.test.showcase.ui.compose.screen.AppNavigation
@ -12,11 +13,16 @@ class ComposeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ProvideWindowInsets {
MaterialTheme {
AppNavigation()
}
}
TestShowCaseApp()
}
}
}
@Composable
fun TestShowCaseApp() {
ProvideWindowInsets {
MaterialTheme {
AppNavigation()
}
}
}

View file

@ -11,6 +11,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
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)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email, imeAction = ImeAction.Next),
onValueChange = { authScreenState.onUsernameChanged(it) },
modifier = Modifier.fillMaxWidth()
modifier = Modifier.fillMaxWidth().testTag(AuthScreenTag.UsernameInput)
)
OutlinedTextField(
value = authScreenState.password,
@ -85,6 +86,7 @@ private fun CredentialsFields(authScreenState: AuthScreenState, modifier: Modifi
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp)
.testTag(AuthScreenTag.PasswordInput)
)
}
}
@ -112,7 +114,7 @@ private fun Snackbar(authScreenState: AuthScreenState, modifier: Modifier = Modi
@Composable
private fun LoginButton(modifier: Modifier = Modifier, onClick: () -> Unit) {
Box(modifier) {
Button(onClick = onClick, Modifier.fillMaxWidth()) {
Button(onClick = onClick, Modifier.fillMaxWidth().testTag(AuthScreenTag.LoginButton)) {
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.UNSUPPORTED_USERNAME -> R.string.username_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"
}