From 5d89e6235639b2ed3bab83e266dd1f96b65a96b5 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Thu, 27 Jan 2022 01:54:43 +0200 Subject: [PATCH] Issue#13 Remove unnecessary configurations and use TestDispatcher on both side Additionally added workaround for progressbar testing --- .../AndroidTestLoginRobotConfiguration.kt | 5 -- .../AndroidTestMainDispatcherTestRule.kt | 51 ------------------- .../SpecificTestConfigurationsFactory.kt | 5 -- .../RobolectricLoginRobotConfiguration.kt | 5 -- .../SpecificTestConfigurationsFactory.kt | 5 -- .../testutils/ActivityScenarioExtensions.kt | 22 ++++++++ .../configuration/LoginRobotConfiguration.kt | 6 --- .../configuration/MainDispatcherTestRule.kt | 18 ------- .../TestConfigurationsFactory.kt | 4 -- .../testutils/doBlockinglyOnMainThread.kt | 2 +- .../idling/MainDispatcherTestRule.kt} | 26 ++++++---- .../testutils/idling/awaitIdlingResources.kt | 19 +++---- .../statesetup/SetupAuthenticationState.kt | 12 ++--- .../testutils/viewactions/PullToRefresh.kt | 4 +- .../ReplaceProgressBarDrawableToStatic.kt | 24 +++++++++ .../test/showcase/ui/home/MainActivityTest.kt | 9 ++-- .../showcase/ui/login/AuthActivityTest.kt | 7 +-- .../test/showcase/ui/login/LoginRobot.kt | 32 ++++++------ .../showcase/ui/splash/SplashActivityTest.kt | 26 ++-------- 19 files changed, 108 insertions(+), 174 deletions(-) delete mode 100644 app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidTestLoginRobotConfiguration.kt delete mode 100644 app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidTestMainDispatcherTestRule.kt delete mode 100644 app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/RobolectricLoginRobotConfiguration.kt create mode 100644 app/src/sharedTest/java/org/fnives/test/showcase/testutils/ActivityScenarioExtensions.kt delete mode 100644 app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/LoginRobotConfiguration.kt delete mode 100644 app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/MainDispatcherTestRule.kt rename app/src/{robolectricTest/java/org/fnives/test/showcase/testutils/configuration/TestCoroutineMainDispatcherTestRule.kt => sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt} (62%) create mode 100644 app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/ReplaceProgressBarDrawableToStatic.kt diff --git a/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidTestLoginRobotConfiguration.kt b/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidTestLoginRobotConfiguration.kt deleted file mode 100644 index edccc1f..0000000 --- a/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidTestLoginRobotConfiguration.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.fnives.test.showcase.testutils.configuration - -object AndroidTestLoginRobotConfiguration : LoginRobotConfiguration { - override val assertLoadingBeforeRequest: Boolean get() = false -} diff --git a/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidTestMainDispatcherTestRule.kt b/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidTestMainDispatcherTestRule.kt deleted file mode 100644 index 2a85cbd..0000000 --- a/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidTestMainDispatcherTestRule.kt +++ /dev/null @@ -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) - } -} diff --git a/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt b/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt index ad369fb..f35cb9d 100644 --- a/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt +++ b/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt @@ -1,11 +1,6 @@ package org.fnives.test.showcase.testutils.configuration object SpecificTestConfigurationsFactory : TestConfigurationsFactory { - override fun createMainDispatcherTestRule(): MainDispatcherTestRule = - AndroidTestMainDispatcherTestRule() - - override fun createLoginRobotConfiguration(): LoginRobotConfiguration = - AndroidTestLoginRobotConfiguration override fun createSnackbarVerification(): SnackbarVerificationHelper = AndroidTestSnackbarVerificationHelper diff --git a/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/RobolectricLoginRobotConfiguration.kt b/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/RobolectricLoginRobotConfiguration.kt deleted file mode 100644 index dbe6927..0000000 --- a/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/RobolectricLoginRobotConfiguration.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.fnives.test.showcase.testutils.configuration - -object RobolectricLoginRobotConfiguration : LoginRobotConfiguration { - override val assertLoadingBeforeRequest: Boolean = true -} diff --git a/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt b/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt index 8df6d58..e7ef0bb 100644 --- a/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt +++ b/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt @@ -1,11 +1,6 @@ package org.fnives.test.showcase.testutils.configuration object SpecificTestConfigurationsFactory : TestConfigurationsFactory { - override fun createMainDispatcherTestRule(): MainDispatcherTestRule = - TestCoroutineMainDispatcherTestRule() - - override fun createLoginRobotConfiguration(): LoginRobotConfiguration = - RobolectricLoginRobotConfiguration override fun createSnackbarVerification(): SnackbarVerificationHelper = RobolectricSnackbarVerificationHelper diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/ActivityScenarioExtensions.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/ActivityScenarioExtensions.kt new file mode 100644 index 0000000..345ea86 --- /dev/null +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/ActivityScenarioExtensions.kt @@ -0,0 +1,22 @@ +package org.fnives.test.showcase.testutils + +import android.app.Activity +import androidx.test.core.app.ActivityScenario + +fun ActivityScenario.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) +} \ No newline at end of file diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/LoginRobotConfiguration.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/LoginRobotConfiguration.kt deleted file mode 100644 index 1cc74df..0000000 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/LoginRobotConfiguration.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.fnives.test.showcase.testutils.configuration - -interface LoginRobotConfiguration { - - val assertLoadingBeforeRequest: Boolean -} diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/MainDispatcherTestRule.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/MainDispatcherTestRule.kt deleted file mode 100644 index 817617a..0000000 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/MainDispatcherTestRule.kt +++ /dev/null @@ -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() diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/TestConfigurationsFactory.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/TestConfigurationsFactory.kt index 84614bb..3dc5eb3 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/TestConfigurationsFactory.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/TestConfigurationsFactory.kt @@ -8,10 +8,6 @@ package org.fnives.test.showcase.testutils.configuration */ interface TestConfigurationsFactory { - fun createMainDispatcherTestRule(): MainDispatcherTestRule - - fun createLoginRobotConfiguration(): LoginRobotConfiguration - fun createSnackbarVerification(): SnackbarVerificationHelper fun createSharedMigrationTestRuleFactory(): SharedMigrationTestRuleFactory diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/doBlockinglyOnMainThread.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/doBlockinglyOnMainThread.kt index f9aa3bd..f8a8e91 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/doBlockinglyOnMainThread.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/doBlockinglyOnMainThread.kt @@ -5,7 +5,7 @@ import android.os.Looper import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.runBlocking -fun doBlockinglyOnMainThread(action: () -> Unit) { +fun runOnUIAwaitOnCurrent(action: () -> Unit) { if (Looper.myLooper() === Looper.getMainLooper()) { action() } else { diff --git a/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/TestCoroutineMainDispatcherTestRule.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt similarity index 62% rename from app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/TestCoroutineMainDispatcherTestRule.kt rename to app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt index 5460a89..68dd4c4 100644 --- a/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/TestCoroutineMainDispatcherTestRule.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt @@ -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.ExperimentalCoroutinesApi @@ -8,12 +8,13 @@ import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain 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.runners.model.Statement @OptIn(ExperimentalCoroutinesApi::class) -class TestCoroutineMainDispatcherTestRule : MainDispatcherTestRule { +class MainDispatcherTestRule : TestRule { private lateinit var testDispatcher: TestDispatcher @@ -33,19 +34,24 @@ class TestCoroutineMainDispatcherTestRule : MainDispatcherTestRule { } } - override fun advanceUntilIdleWithIdlingResources() { + fun advanceUntilIdleWithIdlingResources() = runOnUIAwaitOnCurrent { testDispatcher.advanceUntilIdleWithIdlingResources() } - override fun advanceUntilIdleOrActivityIsDestroyed() { - advanceUntilIdleWithIdlingResources() - } - - override fun advanceUntilIdle() { + fun advanceUntilIdle() = runOnUIAwaitOnCurrent { testDispatcher.scheduler.advanceUntilIdle() } - override fun advanceTimeBy(delayInMillis: Long) { + fun advanceTimeBy(delayInMillis: Long) = runOnUIAwaitOnCurrent { 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() + } } diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/awaitIdlingResources.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/awaitIdlingResources.kt index 80a7d15..b0a1264 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/awaitIdlingResources.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/awaitIdlingResources.kt @@ -1,11 +1,10 @@ package org.fnives.test.showcase.testutils.idling +import android.os.Looper import androidx.test.espresso.Espresso import androidx.test.espresso.IdlingRegistry import androidx.test.espresso.IdlingResource 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.LoopMainThreadUntilIdle 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() { Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainThreadUntilIdle()) // advance until a request is sent while (anyResourceIdling()) { // check if any request is in progress @@ -63,5 +52,9 @@ fun loopMainThreadUntilIdleWithIdlingResources() { } 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)) + } } diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/statesetup/SetupAuthenticationState.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/statesetup/SetupAuthenticationState.kt index ad90612..305d61b 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/statesetup/SetupAuthenticationState.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/statesetup/SetupAuthenticationState.kt @@ -6,7 +6,8 @@ import androidx.test.espresso.intent.Intents import androidx.test.runner.intent.IntentStubberRegistry import org.fnives.test.showcase.network.mockserver.MockServerScenarioSetup 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.home.HomeRobot import org.fnives.test.showcase.ui.home.MainActivity @@ -30,9 +31,9 @@ object SetupAuthenticationState : KoinTest { .setUsername("a") .clickOnLogin() - mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed() + mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() - activityScenario.close() + activityScenario.safeClose() resetIntentsIfNeeded(resetIntents) } @@ -43,10 +44,9 @@ object SetupAuthenticationState : KoinTest { val activityScenario = ActivityScenario.launch(MainActivity::class.java) activityScenario.moveToState(Lifecycle.State.RESUMED) HomeRobot().clickSignOut() + mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() - mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed() - - activityScenario.close() + activityScenario.safeClose() resetIntentsIfNeeded(resetIntents) } diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/PullToRefresh.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/PullToRefresh.kt index 39f9c85..fa8a14c 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/PullToRefresh.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/PullToRefresh.kt @@ -5,7 +5,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.listener import androidx.test.espresso.UiController 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.CoreMatchers.isA import org.hamcrest.Description @@ -36,7 +36,7 @@ class PullToRefresh : ViewAction { override fun perform(uiController: UiController, view: View) { val swipeRefreshLayout = view as SwipeRefreshLayout - doBlockinglyOnMainThread { + runOnUIAwaitOnCurrent { swipeRefreshLayout.isRefreshing = true swipeRefreshLayout.listener().onRefresh() } diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/ReplaceProgressBarDrawableToStatic.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/ReplaceProgressBarDrawableToStatic.kt new file mode 100644 index 0000000..b79192e --- /dev/null +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/ReplaceProgressBarDrawableToStatic.kt @@ -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 = + 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() + } +} \ No newline at end of file diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityTest.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityTest.kt index d8273df..ff4fd8e 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityTest.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityTest.kt @@ -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.refresh.RefreshTokenScenario 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.loopMainThreadUntilIdleWithIdlingResources 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.junit.After import org.junit.Before @@ -45,7 +46,7 @@ class MainActivityTest : KoinTest { @After fun tearDown() { - activityScenario.close() + activityScenario.safeClose() } /** GIVEN initialized MainActivity WHEN signout is clicked THEN user is signed out */ @@ -56,7 +57,7 @@ class MainActivityTest : KoinTest { mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() robot.clickSignOut() - mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed() + mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() robot.assertNavigatedToAuth() } @@ -101,7 +102,7 @@ class MainActivityTest : KoinTest { val expectedItem = FavouriteContent(ContentData.contentSuccess.first(), true) - activityScenario.close() + activityScenario.safeClose() activityScenario = ActivityScenario.launch(MainActivity::class.java) mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/AuthActivityTest.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/AuthActivityTest.kt index f167a58..cfa3e78 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/AuthActivityTest.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/AuthActivityTest.kt @@ -5,8 +5,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import org.fnives.test.showcase.R import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario 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.safeClose import org.fnives.test.showcase.ui.auth.AuthActivity import org.junit.After import org.junit.Rule @@ -34,7 +35,7 @@ class AuthActivityTest : KoinTest { @After 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 */ @@ -52,7 +53,7 @@ class AuthActivityTest : KoinTest { .clickOnLogin() .assertLoadingBeforeRequests() - mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed() + mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() robot.assertNavigatedToHome() } diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/LoginRobot.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/LoginRobot.kt index c14b7de..1210057 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/LoginRobot.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/LoginRobot.kt @@ -15,27 +15,18 @@ import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId 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.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.viewactions.ReplaceProgressBarDrawableToStatic import org.fnives.test.showcase.testutils.viewactions.notIntended import org.fnives.test.showcase.ui.home.MainActivity import org.hamcrest.core.IsNot.not class LoginRobot( - private val loginRobotConfiguration: LoginRobotConfiguration, - private val snackbarVerificationHelper: SnackbarVerificationHelper + private val snackbarVerificationHelper: SnackbarVerificationHelper = SnackbarVerificationTestRule() ) : Robot { - constructor(testConfigurationsFactory: TestConfigurationsFactory = SpecificTestConfigurationsFactory) : - this( - loginRobotConfiguration = testConfigurationsFactory.createLoginRobotConfiguration(), - snackbarVerificationHelper = SnackbarVerificationTestRule() - ) - override fun init() { Intents.init() setupIntentResults() @@ -50,6 +41,18 @@ class LoginRobot( 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 { onView(withId(R.id.user_edit_text)) .perform(ViewActions.replaceText(username), ViewActions.closeSoftKeyboard()) @@ -61,6 +64,7 @@ class LoginRobot( } fun clickOnLogin() = apply { + replaceProgressBar() onView(withId(R.id.login_cta)) .perform(ViewActions.click()) } @@ -80,10 +84,8 @@ class LoginRobot( } fun assertLoadingBeforeRequests() = apply { - if (loginRobotConfiguration.assertLoadingBeforeRequest) { - onView(withId(R.id.loading_indicator)) - .check(ViewAssertions.matches(isDisplayed())) - } + onView(withId(R.id.loading_indicator)) + .check(ViewAssertions.matches(isDisplayed())) } fun assertNotLoading() = apply { diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashActivityTest.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashActivityTest.kt index 2e9fc7b..91ab302 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashActivityTest.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashActivityTest.kt @@ -4,8 +4,9 @@ import androidx.lifecycle.Lifecycle import androidx.test.core.app.ActivityScenario import androidx.test.ext.junit.runners.AndroidJUnit4 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.safeClose import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogin import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogout import org.junit.After @@ -34,7 +35,7 @@ class SplashActivityTest : KoinTest { @After fun tearDown() { - activityScenario.close() + activityScenario.safeClose() } /** GIVEN loggedInState WHEN opened after some time THEN MainActivity is started */ @@ -49,8 +50,6 @@ class SplashActivityTest : KoinTest { robot.assertHomeIsStarted() .assertAuthIsNotStarted() - - workaroundForActivityScenarioCLoseLockingUp() } /** GIVEN loggedOffState WHEN opened after some time THEN AuthActivity is started */ @@ -64,8 +63,6 @@ class SplashActivityTest : KoinTest { robot.assertAuthIsStarted() .assertHomeIsNotStarted() - - workaroundForActivityScenarioCLoseLockingUp() } /** 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.moveToState(Lifecycle.State.RESUMED) - mainDispatcherTestRule.advanceTimeBy(10) + mainDispatcherTestRule.advanceTimeBy(500) robot.assertAuthIsNotStarted() .assertHomeIsNotStarted() @@ -89,22 +86,9 @@ class SplashActivityTest : KoinTest { activityScenario = ActivityScenario.launch(SplashActivity::class.java) activityScenario.moveToState(Lifecycle.State.RESUMED) - mainDispatcherTestRule.advanceTimeBy(10) + mainDispatcherTestRule.advanceTimeBy(500) robot.assertHomeIsNotStarted() .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) - } }