Skip to content

Instantly share code, notes, and snippets.

@10zgurr
Last active November 19, 2022 18:46
Show Gist options
  • Save 10zgurr/4b422009bcd5e4d6a73bbb53ee381af7 to your computer and use it in GitHub Desktop.
Save 10zgurr/4b422009bcd5e4d6a73bbb53ee381af7 to your computer and use it in GitHub Desktop.
package com.theozgurr.apparchitecture.ui.register
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.google.common.truth.Truth.assertThat
import com.theozgurr.apparchitecture.R
import com.theozgurr.apparchitecture.common.clickToTagOwner
import com.theozgurr.apparchitecture.common.saveScreenshot
import com.theozgurr.apparchitecture.common.typeInputToTextFieldWithTag
import com.theozgurr.apparchitecture.data.data_source.user.local.FakeUserLocalDataSourceImplAndroid
import com.theozgurr.apparchitecture.data.data_source.user.remote.FakeUserRemoteDataSourceGenericErrorAndroid
import com.theozgurr.apparchitecture.data.data_source.user.remote.FakeUserRemoteDataSourceLoadingAndroid
import com.theozgurr.apparchitecture.data.data_source.user.remote.FakeUserRemoteDataSourceNetworkErrorAndroid
import com.theozgurr.apparchitecture.data.data_source.user.remote.FakeUserRemoteDataSourceSuccessAndroid
import com.theozgurr.apparchitecture.domain.model.user.UserMapper
import com.theozgurr.apparchitecture.domain.use_case.user.local.InsertUserUseCase
import com.theozgurr.apparchitecture.domain.use_case.user.local.IsLocalUserExistUseCase
import com.theozgurr.apparchitecture.domain.use_case.user.local.UserLocalUseCases
import com.theozgurr.apparchitecture.domain.use_case.user.remote.RegisterUserUseCase
import com.theozgurr.apparchitecture.domain.use_case.user.remote.UserRemoteUseCases
import com.theozgurr.apparchitecture.domain.validation.registration.RegistrationValidator
import com.theozgurr.apparchitecture.ui.MainActivity
import com.theozgurr.apparchitecture.ui.Route
import com.theozgurr.apparchitecture.ui.components.TAG_LOADING_INDICATOR
import com.theozgurr.apparchitecture.ui.home.HomeScreen
import com.theozgurr.apparchitecture.ui.login.LoginScreen
import com.theozgurr.apparchitecture.ui.theme.AppArchitectureJetpackComposeUnitAndUITestingTheme
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Rule
import org.junit.Test
import org.junit.runners.MethodSorters
@ExperimentalComposeUiApi
@ExperimentalCoroutinesApi
@ExperimentalAnimationApi
@HiltAndroidTest
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class RegisterScreenTest {
@get:Rule(order = 0)
val hiltRule = HiltAndroidRule(this)
@get:Rule(order = 1)
val composeRule = createAndroidComposeRule<MainActivity>()
private lateinit var navController: NavHostController
private lateinit var registerViewModel: RegisterViewModel
private lateinit var fakeUserLocalDataSource: FakeUserLocalDataSourceImplAndroid
private lateinit var fakeUserRemoteDataSourceGenericErrorAndroid: FakeUserRemoteDataSourceGenericErrorAndroid
private lateinit var fakeUserRemoteDataSourceLoadingAndroid: FakeUserRemoteDataSourceLoadingAndroid
private lateinit var fakeUserRemoteDataSourceNetworkErrorAndroid: FakeUserRemoteDataSourceNetworkErrorAndroid
private lateinit var fakeUserRemoteDataSourceSuccessAndroid: FakeUserRemoteDataSourceSuccessAndroid
private lateinit var userLocalUseCases: UserLocalUseCases
private lateinit var userRemoteUseCases: UserRemoteUseCases
private lateinit var registerUserUseCase: RegisterUserUseCase
private lateinit var userMapper: UserMapper
private lateinit var registrationValidator: RegistrationValidator
@Before
fun setUp() {
hiltRule.inject()
fakeUserLocalDataSource = FakeUserLocalDataSourceImplAndroid()
fakeUserRemoteDataSourceGenericErrorAndroid = FakeUserRemoteDataSourceGenericErrorAndroid()
fakeUserRemoteDataSourceLoadingAndroid = FakeUserRemoteDataSourceLoadingAndroid()
fakeUserRemoteDataSourceNetworkErrorAndroid = FakeUserRemoteDataSourceNetworkErrorAndroid()
fakeUserRemoteDataSourceSuccessAndroid = FakeUserRemoteDataSourceSuccessAndroid()
userMapper = UserMapper()
registrationValidator = RegistrationValidator()
userLocalUseCases = UserLocalUseCases(
insertUserUseCase = InsertUserUseCase(
dataSource = fakeUserLocalDataSource,
mapper = userMapper
),
isLocalUserExistUseCase = IsLocalUserExistUseCase(
dataSource = fakeUserLocalDataSource
)
)
}
private fun initRegisterViewModelForGenericErrorCase() {
registerUserUseCase = RegisterUserUseCase(
dataSource = fakeUserRemoteDataSourceGenericErrorAndroid
)
initUserRemoteUseCases()
initRegisterViewModel()
}
private fun initRegisterViewModelForLoadingCase() {
registerUserUseCase = RegisterUserUseCase(
dataSource = fakeUserRemoteDataSourceLoadingAndroid
)
initUserRemoteUseCases()
initRegisterViewModel()
}
private fun initRegisterViewModelForNetworkErrorCase() {
registerUserUseCase = RegisterUserUseCase(
dataSource = fakeUserRemoteDataSourceNetworkErrorAndroid
)
initUserRemoteUseCases()
initRegisterViewModel()
}
private fun initRegisterViewModelForSuccessCase() {
registerUserUseCase = RegisterUserUseCase(
dataSource = fakeUserRemoteDataSourceSuccessAndroid
)
initUserRemoteUseCases()
initRegisterViewModel()
}
private fun initUserRemoteUseCases() {
userRemoteUseCases = UserRemoteUseCases(
registerUserUseCase = registerUserUseCase
)
}
private fun initRegisterViewModel() {
registerViewModel = RegisterViewModel(
userLocalUseCases = userLocalUseCases,
userRemoteUseCases = userRemoteUseCases,
registrationValidator = registrationValidator
)
}
private fun launchRegisterScreen() {
composeRule.setContent {
AppArchitectureJetpackComposeUnitAndUITestingTheme {
RegisterScreen(
registerViewModel = registerViewModel,
onNavigateToLoginScreen = {},
onNavigateToHomeScreen = {}
)
}
}
}
private fun launchRegisterScreenWithNavGraph() {
composeRule.setContent {
AppArchitectureJetpackComposeUnitAndUITestingTheme {
navController = rememberNavController()
NavHost(
navController = navController,
startDestination = Route.Register.screen
) {
composable(route = Route.Register.screen) {
RegisterScreen(
registerViewModel = registerViewModel,
onNavigateToLoginScreen = {
navController.navigate(
route = Route.Login.screen
)
},
onNavigateToHomeScreen = {
navController.navigate(
route = Route.Home.screen
) {
popUpTo(Route.Register.screen) {
inclusive = true
}
}
}
)
}
composable(route = Route.Login.screen) {
LoginScreen()
}
composable(route = Route.Home.screen) {
HomeScreen()
}
}
}
}
}
@Test
fun test_registration_registrationForm_isVisible() {
initRegisterViewModelForSuccessCase()
launchRegisterScreen()
with(composeRule) {
onNodeWithTag(TAG_REGISTRATION_FORM)
.assertIsDisplayed()
saveScreenshot(
fileNamePrefix = "registrationForm",
node = onRoot()
)
}
}
@Test
fun test_registration_returns_invalidEmail() {
initRegisterViewModelForSuccessCase()
launchRegisterScreen()
with(composeRule) {
val email = "invalid_email"
typeInputToTextFieldWithTag(
input = email,
tag = TAG_TEXT_FIELD_EMAIL
)
clickToTagOwner(TAG_BUTTON_REGISTER)
val invalidEmail = activity.getString(R.string.invalid_email)
onNodeWithText(invalidEmail)
.assertIsDisplayed()
}
}
@Test
fun test_registration_returns_invalidPassword() {
initRegisterViewModelForSuccessCase()
launchRegisterScreen()
with(composeRule) {
val email = "test@test.com"
val password = "123456"
typeInputToTextFieldWithTag(
input = email,
tag = TAG_TEXT_FIELD_EMAIL
)
typeInputToTextFieldWithTag(
input = password,
tag = TAG_TEXT_FIELD_PASSWORD
)
clickToTagOwner(
tag = TAG_BUTTON_REGISTER
)
val invalidPassword = activity.getString(R.string.invalid_password)
onNodeWithText(invalidPassword)
.assertIsDisplayed()
val passwordRequirements = activity.getString(R.string.password_requirements)
onNodeWithText(passwordRequirements)
.assertIsDisplayed()
saveScreenshot(
fileNamePrefix = "invalidPassword",
node = onRoot()
)
}
}
@Test
fun test_registration_returns_unselectedGender() {
initRegisterViewModelForSuccessCase()
launchRegisterScreen()
with(composeRule) {
val email = "test@test.com"
val password = "123456ab"
typeInputToTextFieldWithTag(
input = email,
tag = TAG_TEXT_FIELD_EMAIL
)
typeInputToTextFieldWithTag(
input = password,
tag = TAG_TEXT_FIELD_PASSWORD
)
clickToTagOwner(
tag = TAG_BUTTON_REGISTER
)
val selectYourGender = activity.getString(R.string.please_select_your_gender)
onNodeWithText(selectYourGender)
.assertIsDisplayed()
saveScreenshot(
fileNamePrefix = "unselectedGender",
node = onRoot()
)
}
}
@Test
fun test_registration_returns_loading() {
initRegisterViewModelForLoadingCase()
launchRegisterScreen()
with(composeRule) {
val email = "test@test.com"
val password = "123456ab"
val age = "24"
typeInputToTextFieldWithTag(
input = email,
tag = TAG_TEXT_FIELD_EMAIL
)
typeInputToTextFieldWithTag(
input = password,
tag = TAG_TEXT_FIELD_PASSWORD
)
typeInputToTextFieldWithTag(
input = age,
tag = TAG_TEXT_FIELD_AGE
)
clickToTagOwner(
tag = TAG_RADIO_BUTTON_FEMALE
)
clickToTagOwner(
tag = TAG_BUTTON_REGISTER
)
onNodeWithTag(TAG_REGISTRATION_FORM)
.assertDoesNotExist()
onNodeWithTag(TAG_LOADING_INDICATOR)
.assertIsDisplayed()
}
}
@Test
fun test_registration_returns_networkError() {
initRegisterViewModelForNetworkErrorCase()
launchRegisterScreen()
with(composeRule) {
val email = "test@test.com"
val password = "123456ab"
val age = "25"
typeInputToTextFieldWithTag(
input = email,
tag = TAG_TEXT_FIELD_EMAIL
)
typeInputToTextFieldWithTag(
input = password,
tag = TAG_TEXT_FIELD_PASSWORD
)
typeInputToTextFieldWithTag(
input = age,
tag = TAG_TEXT_FIELD_AGE
)
clickToTagOwner(
tag = TAG_RADIO_BUTTON_FEMALE
)
clickToTagOwner(
tag = TAG_BUTTON_REGISTER
)
onNodeWithTag(TAG_REGISTRATION_FORM)
.assertIsDisplayed()
onNodeWithTag(TAG_LOADING_INDICATOR)
.assertDoesNotExist()
val noInternetConnection = activity.getString(R.string.no_internet_connection)
onNodeWithText(noInternetConnection)
.assertIsDisplayed()
saveScreenshot(
fileNamePrefix = "networkError",
node = onRoot()
)
}
}
@Test
fun test_registration_returns_genericError() {
initRegisterViewModelForGenericErrorCase()
launchRegisterScreen()
with(composeRule) {
val email = "test@test.com"
val password = "123456ab"
val age = "25"
typeInputToTextFieldWithTag(
input = email,
tag = TAG_TEXT_FIELD_EMAIL
)
typeInputToTextFieldWithTag(
input = password,
tag = TAG_TEXT_FIELD_PASSWORD
)
typeInputToTextFieldWithTag(
input = age,
tag = TAG_TEXT_FIELD_AGE
)
clickToTagOwner(
tag = TAG_RADIO_BUTTON_FEMALE
)
clickToTagOwner(
tag = TAG_BUTTON_REGISTER
)
val requestFailed = activity.getString(R.string.request_failed)
onNodeWithText(requestFailed)
.assertIsDisplayed()
saveScreenshot(
fileNamePrefix = "genericError",
node = onRoot()
)
}
}
@Test
fun test_registration_returns_success_navigateToHomeScreen() {
initRegisterViewModelForSuccessCase()
launchRegisterScreenWithNavGraph()
with(composeRule) {
val email = "test@test.com"
val password = "123456ab"
val age = "25"
typeInputToTextFieldWithTag(
input = email,
tag = TAG_TEXT_FIELD_EMAIL
)
typeInputToTextFieldWithTag(
input = password,
tag = TAG_TEXT_FIELD_PASSWORD
)
typeInputToTextFieldWithTag(
input = age,
tag = TAG_TEXT_FIELD_AGE
)
clickToTagOwner(
tag = TAG_RADIO_BUTTON_FEMALE
)
clickToTagOwner(
tag = TAG_BUTTON_REGISTER
)
waitForIdle()
val route = navController.currentBackStackEntry?.destination?.route
assertThat(route).isEqualTo(Route.Home.screen)
}
}
@Test
fun test_registration_clickOrLoginText_navigateToLoginScreen() {
initRegisterViewModelForSuccessCase()
launchRegisterScreenWithNavGraph()
composeRule.clickToTagOwner(
tag = TAG_OR_LOGIN_TEXT
)
val route = navController.currentBackStackEntry?.destination?.route
assertThat(route).isEqualTo(Route.Login.screen)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment