Issue#11 Adjust SharedTests by using new TestDispatcher instead of deprecated TestDispatchers

This commit is contained in:
Gergely Hegedus 2022-01-23 21:18:30 +02:00
parent 46d9263742
commit 8ae94cfe92
4 changed files with 89 additions and 33 deletions

View file

@ -1,7 +1,10 @@
package org.fnives.test.showcase.testutils.configuration
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.setMain
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.runners.model.Statement
@OptIn(ExperimentalCoroutinesApi::class)
class TestCoroutineMainDispatcherTestRule : MainDispatcherTestRule {
private lateinit var testDispatcher: TestCoroutineDispatcher
private lateinit var testDispatcher: TestDispatcher
override fun apply(base: Statement, description: Description): Statement =
object : Statement() {
@Throws(Throwable::class)
override fun evaluate() {
val dispatcher = TestCoroutineDispatcher()
dispatcher.pauseDispatcher()
val dispatcher = StandardTestDispatcher(TestCoroutineScheduler())
Dispatchers.setMain(dispatcher)
testDispatcher = dispatcher
DatabaseInitialization.dispatcher = dispatcher
@ -39,10 +42,10 @@ class TestCoroutineMainDispatcherTestRule : MainDispatcherTestRule {
}
override fun advanceUntilIdle() {
testDispatcher.advanceUntilIdle()
testDispatcher.scheduler.advanceUntilIdle()
}
override fun advanceTimeBy(delayInMillis: Long) {
testDispatcher.advanceTimeBy(delayInMillis)
testDispatcher.scheduler.advanceTimeBy(delayInMillis)
}
}

View file

@ -4,15 +4,11 @@ import androidx.test.espresso.Espresso
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.IdlingResource
import androidx.test.espresso.matcher.ViewMatchers
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestCoroutineDispatcher
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
private val idleScope = CoroutineScope(Dispatchers.IO)
import java.util.concurrent.Executors
// workaround, issue with idlingResources is tracked here https://github.com/robolectric/robolectric/issues/4807
fun anyResourceIdling(): Boolean = !IdlingRegistry.getInstance().resources.all(IdlingResource::isIdleNow)
@ -21,13 +17,14 @@ fun awaitIdlingResources() {
val idlingRegistry = IdlingRegistry.getInstance()
if (idlingRegistry.resources.all(IdlingResource::isIdleNow)) return
val executor = Executors.newSingleThreadExecutor()
var isIdle = false
idleScope.launch {
executor.submit {
do {
idlingRegistry.resources
.filterNot(IdlingResource::isIdleNow)
.forEach { idlingRegistry ->
idlingRegistry.awaitUntilIdle()
.forEach { idlingResource ->
idlingResource.awaitUntilIdle()
}
} while (!idlingRegistry.resources.all(IdlingResource::isIdleNow))
isIdle = true
@ -35,23 +32,25 @@ fun awaitIdlingResources() {
while (!isIdle) {
loopMainThreadFor(200L)
}
executor.shutdown()
}
private suspend fun IdlingResource.awaitUntilIdle() {
private fun IdlingResource.awaitUntilIdle() {
// using loop because some times, registerIdleTransitionCallback wasn't called
while (true) {
if (isIdleNow) return
delay(100)
Thread.sleep(100L)
}
}
fun TestCoroutineDispatcher.advanceUntilIdleWithIdlingResources() {
advanceUntilIdle() // advance until a request is sent
@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
advanceUntilIdle() // run coroutines after request is finished
scheduler.advanceUntilIdle() // run coroutines after request is finished
}
advanceUntilIdle()
scheduler.advanceUntilIdle()
}
fun loopMainThreadUntilIdleWithIdlingResources() {

View file

@ -78,7 +78,7 @@ class SplashActivityTest : KoinTest {
activityScenario = ActivityScenario.launch(HiltSplashActivity::class.java)
mainDispatcherTestRule.advanceTimeBy(500)
mainDispatcherTestRule.advanceTimeBy(501)
splashRobot.assertHomeIsStarted()
.assertAuthIsNotStarted()
@ -93,9 +93,36 @@ class SplashActivityTest : KoinTest {
activityScenario = ActivityScenario.launch(HiltSplashActivity::class.java)
mainDispatcherTestRule.advanceTimeBy(500)
mainDispatcherTestRule.advanceTimeBy(501)
splashRobot.assertAuthIsStarted()
.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()
}
}

View file

@ -1,6 +1,5 @@
package org.fnives.test.showcase.ui.splash
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4
@ -26,10 +25,6 @@ class SplashActivityTest : KoinTest {
private val splashRobot: SplashRobot get() = robotTestRule.robot
@Rule
@JvmField
val instantTaskExecutorRule = InstantTaskExecutorRule()
@Rule
@JvmField
val robotTestRule = RobotTestRule(SplashRobot())
@ -61,14 +56,15 @@ class SplashActivityTest : KoinTest {
disposable.dispose()
}
/** GIVEN loggedInState WHEN opened THEN MainActivity is started */
/** GIVEN loggedInState WHEN opened after some time THEN MainActivity is started */
@Test
fun loggedInStateNavigatesToHome() {
SetupLoggedInState.setupLogin(mockServerScenarioSetupTestRule.mockServerScenarioSetup)
activityScenario = ActivityScenario.launch(SplashActivity::class.java)
activityScenario.moveToState(Lifecycle.State.RESUMED)
mainDispatcherTestRule.advanceTimeBy(500)
mainDispatcherTestRule.advanceTimeBy(501)
splashRobot.assertHomeIsStarted()
.assertAuthIsNotStarted()
@ -76,16 +72,47 @@ class SplashActivityTest : KoinTest {
SetupLoggedInState.setupLogout()
}
/** GIVEN loggedOffState WHEN opened THEN AuthActivity is started */
/** GIVEN loggedOffState WHEN opened after some time THEN AuthActivity is started */
@Test
fun loggedOutStatesNavigatesToAuthentication() {
SetupLoggedInState.setupLogout()
activityScenario = ActivityScenario.launch(SplashActivity::class.java)
activityScenario.moveToState(Lifecycle.State.RESUMED)
mainDispatcherTestRule.advanceTimeBy(500)
mainDispatcherTestRule.advanceTimeBy(501)
splashRobot.assertAuthIsStarted()
.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()
}
}