Issue#41 Attempt to fix failing tests with compose + update test paths for artifact

This commit is contained in:
Gergely Hegedus 2022-09-28 15:47:43 +03:00
parent 60cfb46ccf
commit f03c9f7bf2
16 changed files with 227 additions and 159 deletions

View file

@ -1,21 +1,18 @@
package org.fnives.test.showcase.ui
import androidx.compose.ui.test.MainTestClock
import androidx.compose.ui.test.junit4.StateRestorationTester
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.espresso.Espresso
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.fnives.test.showcase.R
import org.fnives.test.showcase.android.testutil.intent.DismissSystemDialogsRule
import org.fnives.test.showcase.android.testutil.screenshot.ScreenshotRule
import org.fnives.test.showcase.android.testutil.viewaction.LoopMainThreadFor
import org.fnives.test.showcase.compose.screen.AppNavigation
import org.fnives.test.showcase.core.integration.fake.FakeUserDataLocalStorage
import org.fnives.test.showcase.core.login.IsUserLoggedInUseCase
import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario
import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
import org.fnives.test.showcase.testutils.idling.DatabaseDispatcherTestRule
import org.fnives.test.showcase.ui.compose.idle.ComposeNetworkSynchronizationTestRule
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ -29,7 +26,9 @@ class AuthComposeInstrumentedTest : KoinTest {
private val composeTestRule = createComposeRule()
private val stateRestorationTester = StateRestorationTester(composeTestRule)
private val mockServerScenarioSetupTestRule = MockServerScenarioSetupResetingTestRule()
private val mockServerScenarioSetupTestRule = MockServerScenarioSetupResetingTestRule(
networkSynchronizationTestRule = ComposeNetworkSynchronizationTestRule(composeTestRule)
)
private val mockServerScenarioSetup get() = mockServerScenarioSetupTestRule.mockServerScenarioSetup
private val dispatcherTestRule = DatabaseDispatcherTestRule()
private lateinit var robot: ComposeLoginRobot
@ -72,7 +71,7 @@ class AuthComposeInstrumentedTest : KoinTest {
robot.assertLoading()
composeTestRule.mainClock.autoAdvance = true
composeTestRule.mainClock.awaitIdlingResources()
composeTestRule.waitForIdle()
navigationRobot.assertHomeScreen()
}
@ -86,7 +85,7 @@ class AuthComposeInstrumentedTest : KoinTest {
.assertUsername("banan")
.clickOnLogin()
composeTestRule.mainClock.awaitIdlingResources()
composeTestRule.waitForIdle()
robot.assertErrorIsShown(R.string.password_is_invalid)
.assertNotLoading()
navigationRobot.assertAuthScreen()
@ -103,7 +102,7 @@ class AuthComposeInstrumentedTest : KoinTest {
.assertPassword("banan")
.clickOnLogin()
composeTestRule.mainClock.awaitIdlingResources()
composeTestRule.waitForIdle()
robot.assertErrorIsShown(R.string.username_is_invalid)
.assertNotLoading()
navigationRobot.assertAuthScreen()
@ -129,7 +128,7 @@ class AuthComposeInstrumentedTest : KoinTest {
robot.assertLoading()
composeTestRule.mainClock.autoAdvance = true
composeTestRule.mainClock.awaitIdlingResources()
composeTestRule.waitForIdle()
robot.assertErrorIsShown(R.string.credentials_invalid)
.assertNotLoading()
navigationRobot.assertAuthScreen()
@ -155,7 +154,7 @@ class AuthComposeInstrumentedTest : KoinTest {
robot.assertLoading()
composeTestRule.mainClock.autoAdvance = true
composeTestRule.mainClock.awaitIdlingResources()
composeTestRule.waitForIdle()
robot.assertErrorIsShown(R.string.something_went_wrong)
.assertNotLoading()
navigationRobot.assertAuthScreen()
@ -181,15 +180,5 @@ class AuthComposeInstrumentedTest : KoinTest {
companion object {
private const val SPLASH_DELAY = 600L
// workaround, issue with idlingResources is tracked here https://github.com/robolectric/robolectric/issues/4807
/**
* Await the idling resource on a different thread while looping main.
*/
fun MainTestClock.awaitIdlingResources() {
Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainThreadFor(100L))
advanceTimeByFrame()
}
}
}

View file

@ -1,10 +1,10 @@
package org.fnives.test.showcase.ui
import android.content.Context
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertTextContains
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
@ -13,8 +13,8 @@ import androidx.test.core.app.ApplicationProvider
import org.fnives.test.showcase.compose.screen.auth.AuthScreenTag
class ComposeLoginRobot(
composeTestRule: ComposeTestRule,
) : ComposeTestRule by composeTestRule {
semanticsNodeInteractionsProvider: SemanticsNodeInteractionsProvider,
) : SemanticsNodeInteractionsProvider by semanticsNodeInteractionsProvider {
fun setUsername(username: String): ComposeLoginRobot = apply {
onNodeWithTag(AuthScreenTag.UsernameInput).performTextInput(username)

View file

@ -0,0 +1,22 @@
package org.fnives.test.showcase.ui.compose.idle
import androidx.compose.ui.test.IdlingResource
import androidx.compose.ui.test.junit4.ComposeTestRule
import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.Disposable
class ComposeIdlingDisposable(
private val idlingResource: IdlingResource,
private val testRule: ComposeTestRule,
) : Disposable {
override var isDisposed: Boolean = false
private set
init {
testRule.registerIdlingResource(idlingResource)
}
override fun dispose() {
isDisposed = true
testRule.unregisterIdlingResource(idlingResource)
}
}

View file

@ -0,0 +1,50 @@
package org.fnives.test.showcase.ui.compose.idle
import android.util.Log
import androidx.annotation.CheckResult
import androidx.compose.ui.test.junit4.ComposeTestRule
import okhttp3.OkHttpClient
import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.CompositeDisposable
import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.Disposable
import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.OkHttp3IdlingResource
import org.fnives.test.showcase.network.testutil.NetworkTestConfigurationHelper
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
import org.koin.test.KoinTest
class ComposeNetworkSynchronizationTestRule(private val composeTestRule: ComposeTestRule) : TestRule, KoinTest {
private var disposable: Disposable? = null
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
disposable = registerIdlingResources()
try {
base.evaluate()
} finally {
dispose()
}
}
}
}
fun dispose() {
if (disposable == null) {
Log.w("ComposeNetworkSynchronizationTestRule", "Was disposed, but registerIdlingResources was not called!")
}
disposable?.dispose()
}
@CheckResult
private fun registerIdlingResources(): Disposable = getOkHttpClients()
.associateBy(keySelector = { it.toString() })
.map { (key, client) -> OkHttp3IdlingResource.create(key, client) }
.map(::EspressoToComposeIdlingResourceAdapter)
.map { ComposeIdlingDisposable(it, composeTestRule) }
.let(::CompositeDisposable)
private fun getOkHttpClients(): List<OkHttpClient> =
NetworkTestConfigurationHelper.getOkHttpClients()
}

View file

@ -0,0 +1,7 @@
package org.fnives.test.showcase.ui.compose.idle
import androidx.test.espresso.IdlingResource
class EspressoToComposeIdlingResourceAdapter(private val idlingResource: IdlingResource) : androidx.compose.ui.test.IdlingResource {
override val isIdleNow: Boolean get() = idlingResource.isIdleNow
}