Issue#13 Remove unnecessary configurations and use TestDispatcher on both side
Additionally added workaround for progressbar testing
This commit is contained in:
parent
b9644512d5
commit
5d89e62356
19 changed files with 108 additions and 174 deletions
|
|
@ -1,5 +0,0 @@
|
||||||
package org.fnives.test.showcase.testutils.configuration
|
|
||||||
|
|
||||||
object AndroidTestLoginRobotConfiguration : LoginRobotConfiguration {
|
|
||||||
override val assertLoadingBeforeRequest: Boolean get() = false
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
package org.fnives.test.showcase.testutils.configuration
|
|
||||||
|
|
||||||
import androidx.test.espresso.Espresso
|
|
||||||
import androidx.test.espresso.NoActivityResumedException
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import org.fnives.test.showcase.storage.database.DatabaseInitialization
|
|
||||||
import org.fnives.test.showcase.testutils.idling.loopMainThreadFor
|
|
||||||
import org.fnives.test.showcase.testutils.idling.loopMainThreadUntilIdleWithIdlingResources
|
|
||||||
import org.junit.runner.Description
|
|
||||||
import org.junit.runners.model.Statement
|
|
||||||
|
|
||||||
class AndroidTestMainDispatcherTestRule : MainDispatcherTestRule {
|
|
||||||
|
|
||||||
override fun apply(base: Statement, description: Description): Statement =
|
|
||||||
object : Statement() {
|
|
||||||
@Throws(Throwable::class)
|
|
||||||
override fun evaluate() {
|
|
||||||
DatabaseInitialization.dispatcher = Dispatchers.Main
|
|
||||||
base.evaluate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun advanceUntilIdleWithIdlingResources() {
|
|
||||||
loopMainThreadUntilIdleWithIdlingResources()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun advanceUntilIdleOrActivityIsDestroyed() {
|
|
||||||
try {
|
|
||||||
advanceUntilIdleWithIdlingResources()
|
|
||||||
Espresso.onView(ViewMatchers.isRoot()).check(ViewAssertions.doesNotExist())
|
|
||||||
} catch (noActivityResumedException: NoActivityResumedException) {
|
|
||||||
// expected to happen
|
|
||||||
} catch (runtimeException: RuntimeException) {
|
|
||||||
if (runtimeException.message?.contains("No activities found") == true) {
|
|
||||||
// expected to happen
|
|
||||||
} else {
|
|
||||||
throw runtimeException
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun advanceUntilIdle() {
|
|
||||||
loopMainThreadUntilIdleWithIdlingResources()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun advanceTimeBy(delayInMillis: Long) {
|
|
||||||
loopMainThreadFor(delayInMillis)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
package org.fnives.test.showcase.testutils.configuration
|
package org.fnives.test.showcase.testutils.configuration
|
||||||
|
|
||||||
object SpecificTestConfigurationsFactory : TestConfigurationsFactory {
|
object SpecificTestConfigurationsFactory : TestConfigurationsFactory {
|
||||||
override fun createMainDispatcherTestRule(): MainDispatcherTestRule =
|
|
||||||
AndroidTestMainDispatcherTestRule()
|
|
||||||
|
|
||||||
override fun createLoginRobotConfiguration(): LoginRobotConfiguration =
|
|
||||||
AndroidTestLoginRobotConfiguration
|
|
||||||
|
|
||||||
override fun createSnackbarVerification(): SnackbarVerificationHelper =
|
override fun createSnackbarVerification(): SnackbarVerificationHelper =
|
||||||
AndroidTestSnackbarVerificationHelper
|
AndroidTestSnackbarVerificationHelper
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
package org.fnives.test.showcase.testutils.configuration
|
|
||||||
|
|
||||||
object RobolectricLoginRobotConfiguration : LoginRobotConfiguration {
|
|
||||||
override val assertLoadingBeforeRequest: Boolean = true
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
package org.fnives.test.showcase.testutils.configuration
|
package org.fnives.test.showcase.testutils.configuration
|
||||||
|
|
||||||
object SpecificTestConfigurationsFactory : TestConfigurationsFactory {
|
object SpecificTestConfigurationsFactory : TestConfigurationsFactory {
|
||||||
override fun createMainDispatcherTestRule(): MainDispatcherTestRule =
|
|
||||||
TestCoroutineMainDispatcherTestRule()
|
|
||||||
|
|
||||||
override fun createLoginRobotConfiguration(): LoginRobotConfiguration =
|
|
||||||
RobolectricLoginRobotConfiguration
|
|
||||||
|
|
||||||
override fun createSnackbarVerification(): SnackbarVerificationHelper =
|
override fun createSnackbarVerification(): SnackbarVerificationHelper =
|
||||||
RobolectricSnackbarVerificationHelper
|
RobolectricSnackbarVerificationHelper
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.fnives.test.showcase.testutils
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import androidx.test.core.app.ActivityScenario
|
||||||
|
|
||||||
|
fun <T: Activity> ActivityScenario<T>.safeClose() {
|
||||||
|
workaroundForActivityScenarioCLoseLockingUp()
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This should not be needed, we shouldn't use sleep ever.
|
||||||
|
* However, it seems to be and issue described here: https://github.com/android/android-test/issues/676
|
||||||
|
*
|
||||||
|
* If an activity is finished in code, the ActivityScenario.close() can hang 30 to 45 seconds.
|
||||||
|
* This sleeps let's the Activity finish it state change and unlocks the ActivityScenario.
|
||||||
|
*
|
||||||
|
* As soon as that issue is closed, this should be removed as well.
|
||||||
|
*/
|
||||||
|
private fun workaroundForActivityScenarioCLoseLockingUp() {
|
||||||
|
Thread.sleep(1000L)
|
||||||
|
}
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
package org.fnives.test.showcase.testutils.configuration
|
|
||||||
|
|
||||||
interface LoginRobotConfiguration {
|
|
||||||
|
|
||||||
val assertLoadingBeforeRequest: Boolean
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package org.fnives.test.showcase.testutils.configuration
|
|
||||||
|
|
||||||
import org.junit.rules.TestRule
|
|
||||||
|
|
||||||
interface MainDispatcherTestRule : TestRule {
|
|
||||||
|
|
||||||
fun advanceUntilIdleWithIdlingResources()
|
|
||||||
|
|
||||||
fun advanceUntilIdleOrActivityIsDestroyed()
|
|
||||||
|
|
||||||
fun advanceUntilIdle()
|
|
||||||
|
|
||||||
fun advanceTimeBy(delayInMillis: Long)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("TestFunctionName")
|
|
||||||
fun MainDispatcherTestRule(): MainDispatcherTestRule =
|
|
||||||
SpecificTestConfigurationsFactory.createMainDispatcherTestRule()
|
|
||||||
|
|
@ -8,10 +8,6 @@ package org.fnives.test.showcase.testutils.configuration
|
||||||
*/
|
*/
|
||||||
interface TestConfigurationsFactory {
|
interface TestConfigurationsFactory {
|
||||||
|
|
||||||
fun createMainDispatcherTestRule(): MainDispatcherTestRule
|
|
||||||
|
|
||||||
fun createLoginRobotConfiguration(): LoginRobotConfiguration
|
|
||||||
|
|
||||||
fun createSnackbarVerification(): SnackbarVerificationHelper
|
fun createSnackbarVerification(): SnackbarVerificationHelper
|
||||||
|
|
||||||
fun createSharedMigrationTestRuleFactory(): SharedMigrationTestRuleFactory
|
fun createSharedMigrationTestRuleFactory(): SharedMigrationTestRuleFactory
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import android.os.Looper
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
fun doBlockinglyOnMainThread(action: () -> Unit) {
|
fun runOnUIAwaitOnCurrent(action: () -> Unit) {
|
||||||
if (Looper.myLooper() === Looper.getMainLooper()) {
|
if (Looper.myLooper() === Looper.getMainLooper()) {
|
||||||
action()
|
action()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package org.fnives.test.showcase.testutils.configuration
|
package org.fnives.test.showcase.testutils.idling
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
|
@ -8,12 +8,13 @@ import kotlinx.coroutines.test.TestDispatcher
|
||||||
import kotlinx.coroutines.test.resetMain
|
import kotlinx.coroutines.test.resetMain
|
||||||
import kotlinx.coroutines.test.setMain
|
import kotlinx.coroutines.test.setMain
|
||||||
import org.fnives.test.showcase.storage.database.DatabaseInitialization
|
import org.fnives.test.showcase.storage.database.DatabaseInitialization
|
||||||
import org.fnives.test.showcase.testutils.idling.advanceUntilIdleWithIdlingResources
|
import org.fnives.test.showcase.testutils.runOnUIAwaitOnCurrent
|
||||||
|
import org.junit.rules.TestRule
|
||||||
import org.junit.runner.Description
|
import org.junit.runner.Description
|
||||||
import org.junit.runners.model.Statement
|
import org.junit.runners.model.Statement
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
class TestCoroutineMainDispatcherTestRule : MainDispatcherTestRule {
|
class MainDispatcherTestRule : TestRule {
|
||||||
|
|
||||||
private lateinit var testDispatcher: TestDispatcher
|
private lateinit var testDispatcher: TestDispatcher
|
||||||
|
|
||||||
|
|
@ -33,19 +34,24 @@ class TestCoroutineMainDispatcherTestRule : MainDispatcherTestRule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun advanceUntilIdleWithIdlingResources() {
|
fun advanceUntilIdleWithIdlingResources() = runOnUIAwaitOnCurrent {
|
||||||
testDispatcher.advanceUntilIdleWithIdlingResources()
|
testDispatcher.advanceUntilIdleWithIdlingResources()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun advanceUntilIdleOrActivityIsDestroyed() {
|
fun advanceUntilIdle() = runOnUIAwaitOnCurrent {
|
||||||
advanceUntilIdleWithIdlingResources()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun advanceUntilIdle() {
|
|
||||||
testDispatcher.scheduler.advanceUntilIdle()
|
testDispatcher.scheduler.advanceUntilIdle()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun advanceTimeBy(delayInMillis: Long) {
|
fun advanceTimeBy(delayInMillis: Long) = runOnUIAwaitOnCurrent {
|
||||||
testDispatcher.scheduler.advanceTimeBy(delayInMillis)
|
testDispatcher.scheduler.advanceTimeBy(delayInMillis)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun TestDispatcher.advanceUntilIdleWithIdlingResources() {
|
||||||
|
scheduler.advanceUntilIdle() // advance until a request is sent
|
||||||
|
while (anyResourceIdling()) { // check if any request is in progress
|
||||||
|
awaitIdlingResources() // complete all requests and other idling resources
|
||||||
|
scheduler.advanceUntilIdle() // run coroutines after request is finished
|
||||||
|
}
|
||||||
|
scheduler.advanceUntilIdle()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
package org.fnives.test.showcase.testutils.idling
|
package org.fnives.test.showcase.testutils.idling
|
||||||
|
|
||||||
|
import android.os.Looper
|
||||||
import androidx.test.espresso.Espresso
|
import androidx.test.espresso.Espresso
|
||||||
import androidx.test.espresso.IdlingRegistry
|
import androidx.test.espresso.IdlingRegistry
|
||||||
import androidx.test.espresso.IdlingResource
|
import androidx.test.espresso.IdlingResource
|
||||||
import androidx.test.espresso.matcher.ViewMatchers
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.test.TestDispatcher
|
|
||||||
import org.fnives.test.showcase.testutils.viewactions.LoopMainThreadFor
|
import org.fnives.test.showcase.testutils.viewactions.LoopMainThreadFor
|
||||||
import org.fnives.test.showcase.testutils.viewactions.LoopMainThreadUntilIdle
|
import org.fnives.test.showcase.testutils.viewactions.LoopMainThreadUntilIdle
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
@ -43,16 +42,6 @@ private fun IdlingResource.awaitUntilIdle() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
fun TestDispatcher.advanceUntilIdleWithIdlingResources() {
|
|
||||||
scheduler.advanceUntilIdle() // advance until a request is sent
|
|
||||||
while (anyResourceIdling()) { // check if any request is in progress
|
|
||||||
awaitIdlingResources() // complete all requests and other idling resources
|
|
||||||
scheduler.advanceUntilIdle() // run coroutines after request is finished
|
|
||||||
}
|
|
||||||
scheduler.advanceUntilIdle()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loopMainThreadUntilIdleWithIdlingResources() {
|
fun loopMainThreadUntilIdleWithIdlingResources() {
|
||||||
Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainThreadUntilIdle()) // advance until a request is sent
|
Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainThreadUntilIdle()) // advance until a request is sent
|
||||||
while (anyResourceIdling()) { // check if any request is in progress
|
while (anyResourceIdling()) { // check if any request is in progress
|
||||||
|
|
@ -63,5 +52,9 @@ fun loopMainThreadUntilIdleWithIdlingResources() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loopMainThreadFor(delay: Long) {
|
fun loopMainThreadFor(delay: Long) {
|
||||||
Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainThreadFor(delay))
|
if (Looper.getMainLooper().isCurrentThread){
|
||||||
|
Thread.sleep(200L)
|
||||||
|
} else {
|
||||||
|
Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainThreadFor(delay))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ import androidx.test.espresso.intent.Intents
|
||||||
import androidx.test.runner.intent.IntentStubberRegistry
|
import androidx.test.runner.intent.IntentStubberRegistry
|
||||||
import org.fnives.test.showcase.network.mockserver.MockServerScenarioSetup
|
import org.fnives.test.showcase.network.mockserver.MockServerScenarioSetup
|
||||||
import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario
|
import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario
|
||||||
import org.fnives.test.showcase.testutils.configuration.MainDispatcherTestRule
|
import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule
|
||||||
|
import org.fnives.test.showcase.testutils.safeClose
|
||||||
import org.fnives.test.showcase.ui.auth.AuthActivity
|
import org.fnives.test.showcase.ui.auth.AuthActivity
|
||||||
import org.fnives.test.showcase.ui.home.HomeRobot
|
import org.fnives.test.showcase.ui.home.HomeRobot
|
||||||
import org.fnives.test.showcase.ui.home.MainActivity
|
import org.fnives.test.showcase.ui.home.MainActivity
|
||||||
|
|
@ -30,9 +31,9 @@ object SetupAuthenticationState : KoinTest {
|
||||||
.setUsername("a")
|
.setUsername("a")
|
||||||
.clickOnLogin()
|
.clickOnLogin()
|
||||||
|
|
||||||
mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed()
|
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
|
||||||
|
|
||||||
activityScenario.close()
|
activityScenario.safeClose()
|
||||||
resetIntentsIfNeeded(resetIntents)
|
resetIntentsIfNeeded(resetIntents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,10 +44,9 @@ object SetupAuthenticationState : KoinTest {
|
||||||
val activityScenario = ActivityScenario.launch(MainActivity::class.java)
|
val activityScenario = ActivityScenario.launch(MainActivity::class.java)
|
||||||
activityScenario.moveToState(Lifecycle.State.RESUMED)
|
activityScenario.moveToState(Lifecycle.State.RESUMED)
|
||||||
HomeRobot().clickSignOut()
|
HomeRobot().clickSignOut()
|
||||||
|
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
|
||||||
|
|
||||||
mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed()
|
activityScenario.safeClose()
|
||||||
|
|
||||||
activityScenario.close()
|
|
||||||
resetIntentsIfNeeded(resetIntents)
|
resetIntentsIfNeeded(resetIntents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import androidx.swiperefreshlayout.widget.listener
|
import androidx.swiperefreshlayout.widget.listener
|
||||||
import androidx.test.espresso.UiController
|
import androidx.test.espresso.UiController
|
||||||
import androidx.test.espresso.ViewAction
|
import androidx.test.espresso.ViewAction
|
||||||
import org.fnives.test.showcase.testutils.doBlockinglyOnMainThread
|
import org.fnives.test.showcase.testutils.runOnUIAwaitOnCurrent
|
||||||
import org.hamcrest.BaseMatcher
|
import org.hamcrest.BaseMatcher
|
||||||
import org.hamcrest.CoreMatchers.isA
|
import org.hamcrest.CoreMatchers.isA
|
||||||
import org.hamcrest.Description
|
import org.hamcrest.Description
|
||||||
|
|
@ -36,7 +36,7 @@ class PullToRefresh : ViewAction {
|
||||||
|
|
||||||
override fun perform(uiController: UiController, view: View) {
|
override fun perform(uiController: UiController, view: View) {
|
||||||
val swipeRefreshLayout = view as SwipeRefreshLayout
|
val swipeRefreshLayout = view as SwipeRefreshLayout
|
||||||
doBlockinglyOnMainThread {
|
runOnUIAwaitOnCurrent {
|
||||||
swipeRefreshLayout.isRefreshing = true
|
swipeRefreshLayout.isRefreshing = true
|
||||||
swipeRefreshLayout.listener().onRefresh()
|
swipeRefreshLayout.listener().onRefresh()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.fnives.test.showcase.testutils.viewactions
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ProgressBar
|
||||||
|
import androidx.test.espresso.UiController
|
||||||
|
import androidx.test.espresso.ViewAction
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
|
||||||
|
import org.hamcrest.Matcher
|
||||||
|
|
||||||
|
class ReplaceProgressBarDrawableToStatic : ViewAction {
|
||||||
|
override fun getConstraints(): Matcher<View> =
|
||||||
|
isAssignableFrom(ProgressBar::class.java)
|
||||||
|
|
||||||
|
override fun getDescription(): String =
|
||||||
|
"replace the ProgressBar drawable"
|
||||||
|
|
||||||
|
override fun perform(uiController: UiController, view: View) {
|
||||||
|
val progressBar: ProgressBar = view as ProgressBar
|
||||||
|
progressBar.indeterminateDrawable = ColorDrawable(Color.GREEN)
|
||||||
|
uiController.loopMainThreadUntilIdle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,10 +7,11 @@ import org.fnives.test.showcase.network.mockserver.ContentData
|
||||||
import org.fnives.test.showcase.network.mockserver.scenario.content.ContentScenario
|
import org.fnives.test.showcase.network.mockserver.scenario.content.ContentScenario
|
||||||
import org.fnives.test.showcase.network.mockserver.scenario.refresh.RefreshTokenScenario
|
import org.fnives.test.showcase.network.mockserver.scenario.refresh.RefreshTokenScenario
|
||||||
import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
|
import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
|
||||||
import org.fnives.test.showcase.testutils.configuration.MainDispatcherTestRule
|
import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule
|
||||||
import org.fnives.test.showcase.testutils.idling.loopMainThreadFor
|
import org.fnives.test.showcase.testutils.idling.loopMainThreadFor
|
||||||
import org.fnives.test.showcase.testutils.idling.loopMainThreadUntilIdleWithIdlingResources
|
import org.fnives.test.showcase.testutils.idling.loopMainThreadUntilIdleWithIdlingResources
|
||||||
import org.fnives.test.showcase.testutils.robot.RobotTestRule
|
import org.fnives.test.showcase.testutils.robot.RobotTestRule
|
||||||
|
import org.fnives.test.showcase.testutils.safeClose
|
||||||
import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogin
|
import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogin
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
|
|
@ -45,7 +46,7 @@ class MainActivityTest : KoinTest {
|
||||||
|
|
||||||
@After
|
@After
|
||||||
fun tearDown() {
|
fun tearDown() {
|
||||||
activityScenario.close()
|
activityScenario.safeClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GIVEN initialized MainActivity WHEN signout is clicked THEN user is signed out */
|
/** GIVEN initialized MainActivity WHEN signout is clicked THEN user is signed out */
|
||||||
|
|
@ -56,7 +57,7 @@ class MainActivityTest : KoinTest {
|
||||||
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
|
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
|
||||||
|
|
||||||
robot.clickSignOut()
|
robot.clickSignOut()
|
||||||
mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed()
|
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
|
||||||
|
|
||||||
robot.assertNavigatedToAuth()
|
robot.assertNavigatedToAuth()
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +102,7 @@ class MainActivityTest : KoinTest {
|
||||||
|
|
||||||
val expectedItem = FavouriteContent(ContentData.contentSuccess.first(), true)
|
val expectedItem = FavouriteContent(ContentData.contentSuccess.first(), true)
|
||||||
|
|
||||||
activityScenario.close()
|
activityScenario.safeClose()
|
||||||
activityScenario = ActivityScenario.launch(MainActivity::class.java)
|
activityScenario = ActivityScenario.launch(MainActivity::class.java)
|
||||||
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
|
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import org.fnives.test.showcase.R
|
import org.fnives.test.showcase.R
|
||||||
import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario
|
import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario
|
||||||
import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
|
import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
|
||||||
import org.fnives.test.showcase.testutils.configuration.MainDispatcherTestRule
|
import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule
|
||||||
import org.fnives.test.showcase.testutils.robot.RobotTestRule
|
import org.fnives.test.showcase.testutils.robot.RobotTestRule
|
||||||
|
import org.fnives.test.showcase.testutils.safeClose
|
||||||
import org.fnives.test.showcase.ui.auth.AuthActivity
|
import org.fnives.test.showcase.ui.auth.AuthActivity
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
|
|
@ -34,7 +35,7 @@ class AuthActivityTest : KoinTest {
|
||||||
|
|
||||||
@After
|
@After
|
||||||
fun tearDown() {
|
fun tearDown() {
|
||||||
activityScenario.close()
|
activityScenario.safeClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GIVEN non empty password and username and successful response WHEN signIn THEN no error is shown and navigating to home */
|
/** GIVEN non empty password and username and successful response WHEN signIn THEN no error is shown and navigating to home */
|
||||||
|
|
@ -52,7 +53,7 @@ class AuthActivityTest : KoinTest {
|
||||||
.clickOnLogin()
|
.clickOnLogin()
|
||||||
.assertLoadingBeforeRequests()
|
.assertLoadingBeforeRequests()
|
||||||
|
|
||||||
mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed()
|
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
|
||||||
robot.assertNavigatedToHome()
|
robot.assertNavigatedToHome()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,27 +15,18 @@ import androidx.test.espresso.matcher.ViewMatchers
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
import org.fnives.test.showcase.R
|
import org.fnives.test.showcase.R
|
||||||
import org.fnives.test.showcase.testutils.configuration.LoginRobotConfiguration
|
|
||||||
import org.fnives.test.showcase.testutils.configuration.SnackbarVerificationHelper
|
import org.fnives.test.showcase.testutils.configuration.SnackbarVerificationHelper
|
||||||
import org.fnives.test.showcase.testutils.configuration.SnackbarVerificationTestRule
|
import org.fnives.test.showcase.testutils.configuration.SnackbarVerificationTestRule
|
||||||
import org.fnives.test.showcase.testutils.configuration.SpecificTestConfigurationsFactory
|
|
||||||
import org.fnives.test.showcase.testutils.configuration.TestConfigurationsFactory
|
|
||||||
import org.fnives.test.showcase.testutils.robot.Robot
|
import org.fnives.test.showcase.testutils.robot.Robot
|
||||||
|
import org.fnives.test.showcase.testutils.viewactions.ReplaceProgressBarDrawableToStatic
|
||||||
import org.fnives.test.showcase.testutils.viewactions.notIntended
|
import org.fnives.test.showcase.testutils.viewactions.notIntended
|
||||||
import org.fnives.test.showcase.ui.home.MainActivity
|
import org.fnives.test.showcase.ui.home.MainActivity
|
||||||
import org.hamcrest.core.IsNot.not
|
import org.hamcrest.core.IsNot.not
|
||||||
|
|
||||||
class LoginRobot(
|
class LoginRobot(
|
||||||
private val loginRobotConfiguration: LoginRobotConfiguration,
|
private val snackbarVerificationHelper: SnackbarVerificationHelper = SnackbarVerificationTestRule()
|
||||||
private val snackbarVerificationHelper: SnackbarVerificationHelper
|
|
||||||
) : Robot {
|
) : Robot {
|
||||||
|
|
||||||
constructor(testConfigurationsFactory: TestConfigurationsFactory = SpecificTestConfigurationsFactory) :
|
|
||||||
this(
|
|
||||||
loginRobotConfiguration = testConfigurationsFactory.createLoginRobotConfiguration(),
|
|
||||||
snackbarVerificationHelper = SnackbarVerificationTestRule()
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
Intents.init()
|
Intents.init()
|
||||||
setupIntentResults()
|
setupIntentResults()
|
||||||
|
|
@ -50,6 +41,18 @@ class LoginRobot(
|
||||||
Intents.release()
|
Intents.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Needed because Espresso idling waits until mainThread is idle.
|
||||||
|
*
|
||||||
|
* However, ProgressBar keeps the main thread active since it's animating.
|
||||||
|
*
|
||||||
|
* Another solution is described here: https://proandroiddev.com/progressbar-animations-with-espresso-57f826102187
|
||||||
|
* In short they replace the inflater to remove animations, by using custom test runner.
|
||||||
|
*/
|
||||||
|
fun replaceProgressBar() = apply {
|
||||||
|
onView(withId(R.id.loading_indicator)).perform(ReplaceProgressBarDrawableToStatic())
|
||||||
|
}
|
||||||
|
|
||||||
fun setUsername(username: String): LoginRobot = apply {
|
fun setUsername(username: String): LoginRobot = apply {
|
||||||
onView(withId(R.id.user_edit_text))
|
onView(withId(R.id.user_edit_text))
|
||||||
.perform(ViewActions.replaceText(username), ViewActions.closeSoftKeyboard())
|
.perform(ViewActions.replaceText(username), ViewActions.closeSoftKeyboard())
|
||||||
|
|
@ -61,6 +64,7 @@ class LoginRobot(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clickOnLogin() = apply {
|
fun clickOnLogin() = apply {
|
||||||
|
replaceProgressBar()
|
||||||
onView(withId(R.id.login_cta))
|
onView(withId(R.id.login_cta))
|
||||||
.perform(ViewActions.click())
|
.perform(ViewActions.click())
|
||||||
}
|
}
|
||||||
|
|
@ -80,10 +84,8 @@ class LoginRobot(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun assertLoadingBeforeRequests() = apply {
|
fun assertLoadingBeforeRequests() = apply {
|
||||||
if (loginRobotConfiguration.assertLoadingBeforeRequest) {
|
onView(withId(R.id.loading_indicator))
|
||||||
onView(withId(R.id.loading_indicator))
|
.check(ViewAssertions.matches(isDisplayed()))
|
||||||
.check(ViewAssertions.matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun assertNotLoading() = apply {
|
fun assertNotLoading() = apply {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@ import androidx.lifecycle.Lifecycle
|
||||||
import androidx.test.core.app.ActivityScenario
|
import androidx.test.core.app.ActivityScenario
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
|
import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
|
||||||
import org.fnives.test.showcase.testutils.configuration.MainDispatcherTestRule
|
import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule
|
||||||
import org.fnives.test.showcase.testutils.robot.RobotTestRule
|
import org.fnives.test.showcase.testutils.robot.RobotTestRule
|
||||||
|
import org.fnives.test.showcase.testutils.safeClose
|
||||||
import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogin
|
import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogin
|
||||||
import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogout
|
import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogout
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
|
|
@ -34,7 +35,7 @@ class SplashActivityTest : KoinTest {
|
||||||
|
|
||||||
@After
|
@After
|
||||||
fun tearDown() {
|
fun tearDown() {
|
||||||
activityScenario.close()
|
activityScenario.safeClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GIVEN loggedInState WHEN opened after some time THEN MainActivity is started */
|
/** GIVEN loggedInState WHEN opened after some time THEN MainActivity is started */
|
||||||
|
|
@ -49,8 +50,6 @@ class SplashActivityTest : KoinTest {
|
||||||
|
|
||||||
robot.assertHomeIsStarted()
|
robot.assertHomeIsStarted()
|
||||||
.assertAuthIsNotStarted()
|
.assertAuthIsNotStarted()
|
||||||
|
|
||||||
workaroundForActivityScenarioCLoseLockingUp()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GIVEN loggedOffState WHEN opened after some time THEN AuthActivity is started */
|
/** GIVEN loggedOffState WHEN opened after some time THEN AuthActivity is started */
|
||||||
|
|
@ -64,8 +63,6 @@ class SplashActivityTest : KoinTest {
|
||||||
|
|
||||||
robot.assertAuthIsStarted()
|
robot.assertAuthIsStarted()
|
||||||
.assertHomeIsNotStarted()
|
.assertHomeIsNotStarted()
|
||||||
|
|
||||||
workaroundForActivityScenarioCLoseLockingUp()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GIVEN loggedOffState and not enough time WHEN opened THEN no activity is started */
|
/** GIVEN loggedOffState and not enough time WHEN opened THEN no activity is started */
|
||||||
|
|
@ -75,7 +72,7 @@ class SplashActivityTest : KoinTest {
|
||||||
activityScenario = ActivityScenario.launch(SplashActivity::class.java)
|
activityScenario = ActivityScenario.launch(SplashActivity::class.java)
|
||||||
activityScenario.moveToState(Lifecycle.State.RESUMED)
|
activityScenario.moveToState(Lifecycle.State.RESUMED)
|
||||||
|
|
||||||
mainDispatcherTestRule.advanceTimeBy(10)
|
mainDispatcherTestRule.advanceTimeBy(500)
|
||||||
|
|
||||||
robot.assertAuthIsNotStarted()
|
robot.assertAuthIsNotStarted()
|
||||||
.assertHomeIsNotStarted()
|
.assertHomeIsNotStarted()
|
||||||
|
|
@ -89,22 +86,9 @@ class SplashActivityTest : KoinTest {
|
||||||
activityScenario = ActivityScenario.launch(SplashActivity::class.java)
|
activityScenario = ActivityScenario.launch(SplashActivity::class.java)
|
||||||
activityScenario.moveToState(Lifecycle.State.RESUMED)
|
activityScenario.moveToState(Lifecycle.State.RESUMED)
|
||||||
|
|
||||||
mainDispatcherTestRule.advanceTimeBy(10)
|
mainDispatcherTestRule.advanceTimeBy(500)
|
||||||
|
|
||||||
robot.assertHomeIsNotStarted()
|
robot.assertHomeIsNotStarted()
|
||||||
.assertAuthIsNotStarted()
|
.assertAuthIsNotStarted()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This should not be needed, we shouldn't use sleep ever.
|
|
||||||
* However, it seems to be and issue described here: https://github.com/android/android-test/issues/676
|
|
||||||
*
|
|
||||||
* If an activity is finished in code, the ActivityScenario.close() can hang 30 to 45 seconds.
|
|
||||||
* This sleeps let's the Activity finish it state change and unlocks the ActivityScenario.
|
|
||||||
*
|
|
||||||
* As soon as that issue is closed, this should be removed as well.
|
|
||||||
*/
|
|
||||||
private fun workaroundForActivityScenarioCLoseLockingUp() {
|
|
||||||
Thread.sleep(1000L)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue