Issue#11 Adjust SharedTests by using new TestDispatcher instead of deprecated TestDispatchers
This commit is contained in:
parent
46d9263742
commit
8ae94cfe92
4 changed files with 89 additions and 33 deletions
|
|
@ -1,7 +1,10 @@
|
||||||
package org.fnives.test.showcase.testutils.configuration
|
package org.fnives.test.showcase.testutils.configuration
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.TestCoroutineScheduler
|
||||||
|
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
|
||||||
|
|
@ -9,16 +12,16 @@ import org.fnives.test.showcase.testutils.idling.advanceUntilIdleWithIdlingResou
|
||||||
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)
|
||||||
class TestCoroutineMainDispatcherTestRule : MainDispatcherTestRule {
|
class TestCoroutineMainDispatcherTestRule : MainDispatcherTestRule {
|
||||||
|
|
||||||
private lateinit var testDispatcher: TestCoroutineDispatcher
|
private lateinit var testDispatcher: TestDispatcher
|
||||||
|
|
||||||
override fun apply(base: Statement, description: Description): Statement =
|
override fun apply(base: Statement, description: Description): Statement =
|
||||||
object : Statement() {
|
object : Statement() {
|
||||||
@Throws(Throwable::class)
|
@Throws(Throwable::class)
|
||||||
override fun evaluate() {
|
override fun evaluate() {
|
||||||
val dispatcher = TestCoroutineDispatcher()
|
val dispatcher = StandardTestDispatcher(TestCoroutineScheduler())
|
||||||
dispatcher.pauseDispatcher()
|
|
||||||
Dispatchers.setMain(dispatcher)
|
Dispatchers.setMain(dispatcher)
|
||||||
testDispatcher = dispatcher
|
testDispatcher = dispatcher
|
||||||
DatabaseInitialization.dispatcher = dispatcher
|
DatabaseInitialization.dispatcher = dispatcher
|
||||||
|
|
@ -39,10 +42,10 @@ class TestCoroutineMainDispatcherTestRule : MainDispatcherTestRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun advanceUntilIdle() {
|
override fun advanceUntilIdle() {
|
||||||
testDispatcher.advanceUntilIdle()
|
testDispatcher.scheduler.advanceUntilIdle()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun advanceTimeBy(delayInMillis: Long) {
|
override fun advanceTimeBy(delayInMillis: Long) {
|
||||||
testDispatcher.advanceTimeBy(delayInMillis)
|
testDispatcher.scheduler.advanceTimeBy(delayInMillis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,11 @@ 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.CoroutineScope
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.test.TestDispatcher
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
|
||||||
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
|
||||||
private val idleScope = CoroutineScope(Dispatchers.IO)
|
|
||||||
|
|
||||||
// workaround, issue with idlingResources is tracked here https://github.com/robolectric/robolectric/issues/4807
|
// workaround, issue with idlingResources is tracked here https://github.com/robolectric/robolectric/issues/4807
|
||||||
fun anyResourceIdling(): Boolean = !IdlingRegistry.getInstance().resources.all(IdlingResource::isIdleNow)
|
fun anyResourceIdling(): Boolean = !IdlingRegistry.getInstance().resources.all(IdlingResource::isIdleNow)
|
||||||
|
|
@ -21,13 +17,14 @@ fun awaitIdlingResources() {
|
||||||
val idlingRegistry = IdlingRegistry.getInstance()
|
val idlingRegistry = IdlingRegistry.getInstance()
|
||||||
if (idlingRegistry.resources.all(IdlingResource::isIdleNow)) return
|
if (idlingRegistry.resources.all(IdlingResource::isIdleNow)) return
|
||||||
|
|
||||||
|
val executor = Executors.newSingleThreadExecutor()
|
||||||
var isIdle = false
|
var isIdle = false
|
||||||
idleScope.launch {
|
executor.submit {
|
||||||
do {
|
do {
|
||||||
idlingRegistry.resources
|
idlingRegistry.resources
|
||||||
.filterNot(IdlingResource::isIdleNow)
|
.filterNot(IdlingResource::isIdleNow)
|
||||||
.forEach { idlingRegistry ->
|
.forEach { idlingResource ->
|
||||||
idlingRegistry.awaitUntilIdle()
|
idlingResource.awaitUntilIdle()
|
||||||
}
|
}
|
||||||
} while (!idlingRegistry.resources.all(IdlingResource::isIdleNow))
|
} while (!idlingRegistry.resources.all(IdlingResource::isIdleNow))
|
||||||
isIdle = true
|
isIdle = true
|
||||||
|
|
@ -35,23 +32,25 @@ fun awaitIdlingResources() {
|
||||||
while (!isIdle) {
|
while (!isIdle) {
|
||||||
loopMainThreadFor(200L)
|
loopMainThreadFor(200L)
|
||||||
}
|
}
|
||||||
|
executor.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun IdlingResource.awaitUntilIdle() {
|
private fun IdlingResource.awaitUntilIdle() {
|
||||||
// using loop because some times, registerIdleTransitionCallback wasn't called
|
// using loop because some times, registerIdleTransitionCallback wasn't called
|
||||||
while (true) {
|
while (true) {
|
||||||
if (isIdleNow) return
|
if (isIdleNow) return
|
||||||
delay(100)
|
Thread.sleep(100L)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TestCoroutineDispatcher.advanceUntilIdleWithIdlingResources() {
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
advanceUntilIdle() // advance until a request is sent
|
fun TestDispatcher.advanceUntilIdleWithIdlingResources() {
|
||||||
|
scheduler.advanceUntilIdle() // advance until a request is sent
|
||||||
while (anyResourceIdling()) { // check if any request is in progress
|
while (anyResourceIdling()) { // check if any request is in progress
|
||||||
awaitIdlingResources() // complete all requests and other idling resources
|
awaitIdlingResources() // complete all requests and other idling resources
|
||||||
advanceUntilIdle() // run coroutines after request is finished
|
scheduler.advanceUntilIdle() // run coroutines after request is finished
|
||||||
}
|
}
|
||||||
advanceUntilIdle()
|
scheduler.advanceUntilIdle()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loopMainThreadUntilIdleWithIdlingResources() {
|
fun loopMainThreadUntilIdleWithIdlingResources() {
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ class SplashActivityTest : KoinTest {
|
||||||
|
|
||||||
activityScenario = ActivityScenario.launch(HiltSplashActivity::class.java)
|
activityScenario = ActivityScenario.launch(HiltSplashActivity::class.java)
|
||||||
|
|
||||||
mainDispatcherTestRule.advanceTimeBy(500)
|
mainDispatcherTestRule.advanceTimeBy(501)
|
||||||
|
|
||||||
splashRobot.assertHomeIsStarted()
|
splashRobot.assertHomeIsStarted()
|
||||||
.assertAuthIsNotStarted()
|
.assertAuthIsNotStarted()
|
||||||
|
|
@ -93,9 +93,36 @@ class SplashActivityTest : KoinTest {
|
||||||
|
|
||||||
activityScenario = ActivityScenario.launch(HiltSplashActivity::class.java)
|
activityScenario = ActivityScenario.launch(HiltSplashActivity::class.java)
|
||||||
|
|
||||||
mainDispatcherTestRule.advanceTimeBy(500)
|
mainDispatcherTestRule.advanceTimeBy(501)
|
||||||
|
|
||||||
splashRobot.assertAuthIsStarted()
|
splashRobot.assertAuthIsStarted()
|
||||||
.assertHomeIsNotStarted()
|
.assertHomeIsNotStarted()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun loggedOutStatesNotEnoughTime() {
|
||||||
|
setupLoggedInState.setupLogout()
|
||||||
|
|
||||||
|
activityScenario = ActivityScenario.launch(HiltSplashActivity::class.java)
|
||||||
|
|
||||||
|
mainDispatcherTestRule.advanceTimeBy(10)
|
||||||
|
|
||||||
|
splashRobot.assertAuthIsNotStarted()
|
||||||
|
.assertHomeIsNotStarted()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GIVEN loggedInState and not enough time WHEN opened THEN no activity is started */
|
||||||
|
@Test
|
||||||
|
fun loggedInStatesNotEnoughTime() {
|
||||||
|
setupLoggedInState.setupLogin(mockServerScenarioSetupTestRule.mockServerScenarioSetup)
|
||||||
|
|
||||||
|
activityScenario = ActivityScenario.launch(HiltSplashActivity::class.java)
|
||||||
|
|
||||||
|
mainDispatcherTestRule.advanceTimeBy(10)
|
||||||
|
|
||||||
|
splashRobot.assertHomeIsNotStarted()
|
||||||
|
.assertAuthIsNotStarted()
|
||||||
|
|
||||||
|
setupLoggedInState.setupLogout()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package org.fnives.test.showcase.ui.splash
|
package org.fnives.test.showcase.ui.splash
|
||||||
|
|
||||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
|
||||||
import androidx.lifecycle.Lifecycle
|
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
|
||||||
|
|
@ -26,10 +25,6 @@ class SplashActivityTest : KoinTest {
|
||||||
|
|
||||||
private val splashRobot: SplashRobot get() = robotTestRule.robot
|
private val splashRobot: SplashRobot get() = robotTestRule.robot
|
||||||
|
|
||||||
@Rule
|
|
||||||
@JvmField
|
|
||||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val robotTestRule = RobotTestRule(SplashRobot())
|
val robotTestRule = RobotTestRule(SplashRobot())
|
||||||
|
|
@ -61,14 +56,15 @@ class SplashActivityTest : KoinTest {
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GIVEN loggedInState WHEN opened THEN MainActivity is started */
|
/** GIVEN loggedInState WHEN opened after some time THEN MainActivity is started */
|
||||||
@Test
|
@Test
|
||||||
fun loggedInStateNavigatesToHome() {
|
fun loggedInStateNavigatesToHome() {
|
||||||
SetupLoggedInState.setupLogin(mockServerScenarioSetupTestRule.mockServerScenarioSetup)
|
SetupLoggedInState.setupLogin(mockServerScenarioSetupTestRule.mockServerScenarioSetup)
|
||||||
|
|
||||||
activityScenario = ActivityScenario.launch(SplashActivity::class.java)
|
activityScenario = ActivityScenario.launch(SplashActivity::class.java)
|
||||||
|
activityScenario.moveToState(Lifecycle.State.RESUMED)
|
||||||
|
|
||||||
mainDispatcherTestRule.advanceTimeBy(500)
|
mainDispatcherTestRule.advanceTimeBy(501)
|
||||||
|
|
||||||
splashRobot.assertHomeIsStarted()
|
splashRobot.assertHomeIsStarted()
|
||||||
.assertAuthIsNotStarted()
|
.assertAuthIsNotStarted()
|
||||||
|
|
@ -76,16 +72,47 @@ class SplashActivityTest : KoinTest {
|
||||||
SetupLoggedInState.setupLogout()
|
SetupLoggedInState.setupLogout()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GIVEN loggedOffState WHEN opened THEN AuthActivity is started */
|
/** GIVEN loggedOffState WHEN opened after some time THEN AuthActivity is started */
|
||||||
@Test
|
@Test
|
||||||
fun loggedOutStatesNavigatesToAuthentication() {
|
fun loggedOutStatesNavigatesToAuthentication() {
|
||||||
SetupLoggedInState.setupLogout()
|
SetupLoggedInState.setupLogout()
|
||||||
|
|
||||||
activityScenario = ActivityScenario.launch(SplashActivity::class.java)
|
activityScenario = ActivityScenario.launch(SplashActivity::class.java)
|
||||||
|
activityScenario.moveToState(Lifecycle.State.RESUMED)
|
||||||
|
|
||||||
mainDispatcherTestRule.advanceTimeBy(500)
|
mainDispatcherTestRule.advanceTimeBy(501)
|
||||||
|
|
||||||
splashRobot.assertAuthIsStarted()
|
splashRobot.assertAuthIsStarted()
|
||||||
.assertHomeIsNotStarted()
|
.assertHomeIsNotStarted()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** GIVEN loggedOffState and not enough time WHEN opened THEN no activity is started */
|
||||||
|
@Test
|
||||||
|
fun loggedOutStatesNotEnoughTime() {
|
||||||
|
SetupLoggedInState.setupLogout()
|
||||||
|
|
||||||
|
activityScenario = ActivityScenario.launch(SplashActivity::class.java)
|
||||||
|
activityScenario.moveToState(Lifecycle.State.RESUMED)
|
||||||
|
|
||||||
|
mainDispatcherTestRule.advanceTimeBy(10)
|
||||||
|
|
||||||
|
splashRobot.assertAuthIsNotStarted()
|
||||||
|
.assertHomeIsNotStarted()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** GIVEN loggedInState and not enough time WHEN opened THEN no activity is started */
|
||||||
|
@Test
|
||||||
|
fun loggedInStatesNotEnoughTime() {
|
||||||
|
SetupLoggedInState.setupLogin(mockServerScenarioSetupTestRule.mockServerScenarioSetup)
|
||||||
|
|
||||||
|
activityScenario = ActivityScenario.launch(SplashActivity::class.java)
|
||||||
|
activityScenario.moveToState(Lifecycle.State.RESUMED)
|
||||||
|
|
||||||
|
mainDispatcherTestRule.advanceTimeBy(10)
|
||||||
|
|
||||||
|
splashRobot.assertHomeIsNotStarted()
|
||||||
|
.assertAuthIsNotStarted()
|
||||||
|
|
||||||
|
SetupLoggedInState.setupLogout()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue