Add a test for restoring compose content

This commit is contained in:
Alex Gabor 2022-04-04 18:16:06 +03:00
parent 2e97716b48
commit 47037d4bcd
3 changed files with 73 additions and 3 deletions

View file

@ -1,5 +1,6 @@
package org.fnives.test.showcase.ui package org.fnives.test.showcase.ui
import androidx.compose.ui.test.junit4.StateRestorationTester
import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import org.fnives.test.showcase.R import org.fnives.test.showcase.R
@ -22,6 +23,7 @@ class AuthComposeInstrumentedTest : KoinTest {
@get:Rule @get:Rule
val composeTestRule = createComposeRule() val composeTestRule = createComposeRule()
private val stateRestorationTester = StateRestorationTester(composeTestRule)
private val mockServerScenarioSetupTestRule = MockServerScenarioSetupResetingTestRule() private val mockServerScenarioSetupTestRule = MockServerScenarioSetupResetingTestRule()
private val mockServerScenarioSetup get() = mockServerScenarioSetupTestRule.mockServerScenarioSetup private val mockServerScenarioSetup get() = mockServerScenarioSetupTestRule.mockServerScenarioSetup
@ -37,7 +39,7 @@ class AuthComposeInstrumentedTest : KoinTest {
@Before @Before
fun setup() { fun setup() {
composeTestRule.setContent { stateRestorationTester.setContent {
AppNavigation(isUserLogeInUseCase = IsUserLoggedInUseCase(FakeUserDataLocalStorage())) AppNavigation(isUserLogeInUseCase = IsUserLoggedInUseCase(FakeUserDataLocalStorage()))
} }
robot = ComposeLoginRobot(composeTestRule) robot = ComposeLoginRobot(composeTestRule)
@ -152,4 +154,21 @@ class AuthComposeInstrumentedTest : KoinTest {
.assertNotLoading() .assertNotLoading()
navigationRobot.assertAuthScreen() navigationRobot.assertAuthScreen()
} }
/** GIVEN username and password WHEN restoring THEN username and password fields contain the same text */
@Test
fun restoringContentShowPreviousCredentials() {
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
navigationRobot.assertAuthScreen()
robot.setUsername("alma")
.setPassword("banan")
.assertUsername("alma")
.assertPassword("banan")
stateRestorationTester.emulateSavedInstanceStateRestore()
navigationRobot.assertAuthScreen()
robot.assertUsername("alma")
.assertPassword("banan")
}
} }

View file

@ -1,7 +1,9 @@
package org.fnives.test.showcase.compose.screen.auth package org.fnives.test.showcase.compose.screen.auth
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.platform.AndroidUiDispatcher import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.mapSaver
import androidx.compose.runtime.saveable.rememberSaveable
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -17,7 +19,9 @@ fun rememberAuthScreenState(
loginUseCase: LoginUseCase = get(), loginUseCase: LoginUseCase = get(),
onLoginSuccess: () -> Unit = {}, onLoginSuccess: () -> Unit = {},
): AuthScreenState { ): AuthScreenState {
return remember { AuthScreenState(stateScope, loginUseCase, onLoginSuccess) } return rememberSaveable(saver = AuthScreenState.getSaver(stateScope, loginUseCase, onLoginSuccess)) {
AuthScreenState(stateScope, loginUseCase, onLoginSuccess)
}
} }
class AuthScreenState( class AuthScreenState(
@ -80,4 +84,23 @@ class AuthScreenState(
UNSUPPORTED_USERNAME, UNSUPPORTED_USERNAME,
UNSUPPORTED_PASSWORD UNSUPPORTED_PASSWORD
} }
companion object {
private const val USERNAME = "USERNAME"
private const val PASSWORD = "PASSWORD"
fun getSaver(
stateScope: CoroutineScope,
loginUseCase: LoginUseCase,
onLoginSuccess: () -> Unit,
): Saver<AuthScreenState, *> = mapSaver(
save = { mapOf(USERNAME to it.username, PASSWORD to it.password) },
restore = {
AuthScreenState(stateScope, loginUseCase, onLoginSuccess).apply {
onUsernameChanged(it.getOrElse(USERNAME) { "" } as String)
onPasswordChanged(it.getOrElse(PASSWORD) { "" } as String)
}
}
)
}
} }

View file

@ -324,3 +324,31 @@ robot.assertErrorIsShown(R.string.something_went_wrong)
.assertNotLoading() .assertNotLoading()
navigationRobot.assertAuthScreen() navigationRobot.assertAuthScreen()
``` ```
### 6. `restoringContentShowPreviousCredentials`
Since we're writing apps for Android, we must handle state restoration so let's write a test for it.
For simulating the recreation of the UI, we first need a `StateRestorationTester`:
```kotlin
private val stateRestorationTester = StateRestorationTester(composeTestRule)
```
Then in `setup()`, we need to `setContent` on `stateRestorationTester` instead of on `composeTestRule`.
Now for the actual test, we first setup the content then we trigger restoration by calling `stateRestorationTester.emulateSavedInstanceStateRestore()`, afterwards we can verify that the content is recreated in the correct way:
```kotlin
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
navigationRobot.assertAuthScreen()
robot.setUsername("alma")
.setPassword("banan")
.assertUsername("alma")
.assertPassword("banan")
stateRestorationTester.emulateSavedInstanceStateRestore()
navigationRobot.assertAuthScreen()
robot.assertUsername("alma")
.assertPassword("banan")
```