Issue#13 Simplify rule sets to minimal and appy them in order

This commit is contained in:
Gergely Hegedus 2022-01-27 00:09:36 +02:00
parent d8b9fadcbc
commit 5a08525e42
10 changed files with 140 additions and 157 deletions

View file

@ -4,7 +4,9 @@ import okhttp3.OkHttpClient
import okhttp3.tls.HandshakeCertificates import okhttp3.tls.HandshakeCertificates
import org.fnives.test.showcase.model.network.BaseUrl import org.fnives.test.showcase.model.network.BaseUrl
import org.fnives.test.showcase.network.mockserver.MockServerScenarioSetup import org.fnives.test.showcase.network.mockserver.MockServerScenarioSetup
import org.fnives.test.showcase.testutils.idling.NetworkSynchronization.OkHttpClientTypes import org.fnives.test.showcase.testutils.idling.NetworkSynchronizationTestRule
import org.fnives.test.showcase.testutils.idling.NetworkSynchronizationTestRule.OkHttpClientTypes
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
import org.koin.core.context.loadKoinModules import org.koin.core.context.loadKoinModules
@ -12,14 +14,26 @@ import org.koin.dsl.module
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
class MockServerScenarioSetupTestRule : ReloadKoinModulesIfNecessaryTestRule(), KoinTest { /**
* TestRule which ensures Koin is reseted between each tests and setups Network mocking.
*
* It First resets koin if needed.
* Then creates and starts the mockwebserver, it also injects the correct baseUrl into the OkHttp Client.
* Then synchronizes Espresso with the OkHttp Client
*/
class MockServerScenarioSetupResetingTestRule(
private val reloadKoinModulesIfNecessaryTestRule: ReloadKoinModulesIfNecessaryTestRule = ReloadKoinModulesIfNecessaryTestRule(),
private val networkSynchronizationTestRule: NetworkSynchronizationTestRule = NetworkSynchronizationTestRule()
) : TestRule, KoinTest {
lateinit var mockServerScenarioSetup: MockServerScenarioSetup lateinit var mockServerScenarioSetup: MockServerScenarioSetup
private val sessionlessQualifier get() = OkHttpClientTypes.SESSIONLESS.asQualifier() private val sessionlessQualifier get() = OkHttpClientTypes.SESSIONLESS.asQualifier()
override fun apply(base: Statement, description: Description): Statement = override fun apply(base: Statement, description: Description): Statement =
super.apply(createStatement(base), description) networkSynchronizationTestRule.apply(base, description)
.let(::createStatement)
.let { reloadKoinModulesIfNecessaryTestRule.apply(it, description) }
private fun createStatement(base: Statement) = object : Statement() { private fun createStatement(base: Statement) = object : Statement() {
@Throws(Throwable::class) @Throws(Throwable::class)

View file

@ -9,6 +9,7 @@ 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
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.core.context.GlobalContext
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin import org.koin.core.context.stopKoin
import org.koin.mp.KoinPlatformTools import org.koin.mp.KoinPlatformTools
@ -22,7 +23,7 @@ import org.koin.test.KoinTest
* *
* Note: Do not use if you want your test's to share Koin, and in such case do not stop your Koin. * Note: Do not use if you want your test's to share Koin, and in such case do not stop your Koin.
*/ */
open class ReloadKoinModulesIfNecessaryTestRule : TestRule, KoinTest { class ReloadKoinModulesIfNecessaryTestRule : TestRule, KoinTest {
override fun apply(base: Statement, description: Description): Statement = override fun apply(base: Statement, description: Description): Statement =
ReinitKoinStatement(base) ReinitKoinStatement(base)
@ -38,6 +39,7 @@ open class ReloadKoinModulesIfNecessaryTestRule : TestRule, KoinTest {
private fun reinitKoinIfNeeded() { private fun reinitKoinIfNeeded() {
if (KoinPlatformTools.defaultContext().getOrNull() != null) return if (KoinPlatformTools.defaultContext().getOrNull() != null) return
if (GlobalContext.getOrNull() != null) return
val application = ApplicationProvider.getApplicationContext<TestShowcaseApplication>() val application = ApplicationProvider.getApplicationContext<TestShowcaseApplication>()
val baseUrl = BaseUrl(BuildConfig.BASE_URL) val baseUrl = BaseUrl(BuildConfig.BASE_URL)

View file

@ -12,3 +12,7 @@ interface MainDispatcherTestRule : TestRule {
fun advanceTimeBy(delayInMillis: Long) fun advanceTimeBy(delayInMillis: Long)
} }
@Suppress("TestFunctionName")
fun MainDispatcherTestRule(): MainDispatcherTestRule =
SpecificTestConfigurationsFactory.createMainDispatcherTestRule()

View file

@ -3,14 +3,35 @@ package org.fnives.test.showcase.testutils.idling
import androidx.annotation.CheckResult import androidx.annotation.CheckResult
import androidx.test.espresso.IdlingResource import androidx.test.espresso.IdlingResource
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
import org.koin.core.qualifier.StringQualifier import org.koin.core.qualifier.StringQualifier
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
object NetworkSynchronization : KoinTest { class NetworkSynchronizationTestRule : TestRule, KoinTest {
private var disposable: Disposable? = null
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
disposable = registerNetworkingSynchronization()
try {
base.evaluate()
} finally {
dispose()
}
}
}
}
fun dispose() = disposable?.dispose()
@CheckResult @CheckResult
fun registerNetworkingSynchronization(): Disposable { private fun registerNetworkingSynchronization(): Disposable {
val idlingResources = OkHttpClientTypes.values() val idlingResources = OkHttpClientTypes.values()
.map { it to getOkHttpClient(it) } .map { it to getOkHttpClient(it) }
.associateBy { it.second.dispatcher } .associateBy { it.second.dispatcher }

View file

@ -2,6 +2,8 @@ package org.fnives.test.showcase.testutils.statesetup
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ActivityScenario
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.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.configuration.MainDispatcherTestRule
@ -15,7 +17,8 @@ object SetupAuthenticationState : KoinTest {
fun setupLogin( fun setupLogin(
mainDispatcherTestRule: MainDispatcherTestRule, mainDispatcherTestRule: MainDispatcherTestRule,
mockServerScenarioSetup: MockServerScenarioSetup mockServerScenarioSetup: MockServerScenarioSetup,
resetIntents: Boolean = true
) { ) {
mockServerScenarioSetup.setScenario(AuthScenario.Success(username = "a", password = "b")) mockServerScenarioSetup.setScenario(AuthScenario.Success(username = "a", password = "b"))
val activityScenario = ActivityScenario.launch(AuthActivity::class.java) val activityScenario = ActivityScenario.launch(AuthActivity::class.java)
@ -30,19 +33,27 @@ object SetupAuthenticationState : KoinTest {
mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed() mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed()
activityScenario.close() activityScenario.close()
resetIntentsIfNeeded(resetIntents)
} }
fun setupLogout( fun setupLogout(
mainDispatcherTestRule: MainDispatcherTestRule mainDispatcherTestRule: MainDispatcherTestRule,
resetIntents: Boolean = true
) { ) {
val activityScenario = ActivityScenario.launch(MainActivity::class.java) val activityScenario = ActivityScenario.launch(MainActivity::class.java)
activityScenario.moveToState(Lifecycle.State.RESUMED) activityScenario.moveToState(Lifecycle.State.RESUMED)
val homeRobot = HomeRobot() HomeRobot().clickSignOut()
homeRobot
.clickSignOut()
mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed() mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed()
activityScenario.close() activityScenario.close()
resetIntentsIfNeeded(resetIntents)
}
private fun resetIntentsIfNeeded(resetIntents: Boolean) {
if (resetIntents && IntentStubberRegistry.isLoaded()) {
Intents.release()
Intents.init()
}
} }
} }

View file

@ -19,10 +19,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withText
import org.fnives.test.showcase.R import org.fnives.test.showcase.R
import org.fnives.test.showcase.model.content.Content import org.fnives.test.showcase.model.content.Content
import org.fnives.test.showcase.model.content.FavouriteContent import org.fnives.test.showcase.model.content.FavouriteContent
import org.fnives.test.showcase.network.mockserver.MockServerScenarioSetup
import org.fnives.test.showcase.testutils.configuration.MainDispatcherTestRule
import org.fnives.test.showcase.testutils.robot.Robot import org.fnives.test.showcase.testutils.robot.Robot
import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState
import org.fnives.test.showcase.testutils.viewactions.PullToRefresh import org.fnives.test.showcase.testutils.viewactions.PullToRefresh
import org.fnives.test.showcase.testutils.viewactions.WithDrawable import org.fnives.test.showcase.testutils.viewactions.WithDrawable
import org.fnives.test.showcase.testutils.viewactions.notIntended import org.fnives.test.showcase.testutils.viewactions.notIntended
@ -33,8 +30,6 @@ class HomeRobot : Robot {
override fun init() { override fun init() {
Intents.init() Intents.init()
Intents.intending(IntentMatchers.hasComponent(AuthActivity::class.java.canonicalName))
.respondWith(Instrumentation.ActivityResult(0, null))
} }
override fun release() { override fun release() {
@ -50,6 +45,9 @@ class HomeRobot : Robot {
} }
fun clickSignOut() = apply { fun clickSignOut() = apply {
Intents.intending(IntentMatchers.hasComponent(AuthActivity::class.java.canonicalName))
.respondWith(Instrumentation.ActivityResult(0, null))
Espresso.onView(withId(R.id.logout_cta)).perform(click()) Espresso.onView(withId(R.id.logout_cta)).perform(click())
} }
@ -103,11 +101,4 @@ class HomeRobot : Robot {
Espresso.onView(withId(R.id.error_message)) Espresso.onView(withId(R.id.error_message))
.check(matches(allOf(isDisplayed(), withText(R.string.something_went_wrong)))) .check(matches(allOf(isDisplayed(), withText(R.string.something_went_wrong))))
} }
fun setupLogin(
mainDispatcherTestRule: MainDispatcherTestRule,
mockServerScenarioSetup: MockServerScenarioSetup
) {
SetupAuthenticationState.setupLogin(mainDispatcherTestRule, mockServerScenarioSetup)
}
} }

View file

@ -6,17 +6,17 @@ import org.fnives.test.showcase.model.content.FavouriteContent
import org.fnives.test.showcase.network.mockserver.ContentData 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.MockServerScenarioSetupTestRule import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
import org.fnives.test.showcase.testutils.configuration.SpecificTestConfigurationsFactory import org.fnives.test.showcase.testutils.configuration.MainDispatcherTestRule
import org.fnives.test.showcase.testutils.idling.Disposable
import org.fnives.test.showcase.testutils.idling.NetworkSynchronization
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.statesetup.SetupAuthenticationState.setupLogin
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.koin.test.KoinTest import org.koin.test.KoinTest
@ -26,37 +26,26 @@ class MainActivityTest : KoinTest {
private lateinit var activityScenario: ActivityScenario<MainActivity> private lateinit var activityScenario: ActivityScenario<MainActivity>
@Rule private val mockServerScenarioSetupTestRule = MockServerScenarioSetupResetingTestRule()
@JvmField private val mockServerScenarioSetup
val robotRule = RobotTestRule(HomeRobot()) get() = mockServerScenarioSetupTestRule.mockServerScenarioSetup
private val homeRobot get() = robotRule.robot private val mainDispatcherTestRule = MainDispatcherTestRule()
private val robot = HomeRobot()
@Rule @Rule
@JvmField @JvmField
val mockServerScenarioSetupTestRule = MockServerScenarioSetupTestRule() val ruleOrder: RuleChain = RuleChain.outerRule(mockServerScenarioSetupTestRule)
.around(mainDispatcherTestRule)
val mockServerScenarioSetup get() = mockServerScenarioSetupTestRule.mockServerScenarioSetup .around(RobotTestRule(robot))
@Rule
@JvmField
val mainDispatcherTestRule = SpecificTestConfigurationsFactory.createMainDispatcherTestRule()
private lateinit var disposable: Disposable
@Before @Before
fun setUp() { fun setup() {
setupLogin(mainDispatcherTestRule, mockServerScenarioSetup)
disposable = NetworkSynchronization.registerNetworkingSynchronization()
homeRobot.setupLogin(
mainDispatcherTestRule,
mockServerScenarioSetup
)
} }
@After @After
fun tearDown() { fun tearDown() {
activityScenario.close() activityScenario.close()
disposable.dispose()
} }
/** GIVEN initialized MainActivity WHEN signout is clicked THEN user is signed out */ /** GIVEN initialized MainActivity WHEN signout is clicked THEN user is signed out */
@ -66,10 +55,10 @@ class MainActivityTest : KoinTest {
activityScenario = ActivityScenario.launch(MainActivity::class.java) activityScenario = ActivityScenario.launch(MainActivity::class.java)
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
homeRobot.clickSignOut() robot.clickSignOut()
mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed() mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed()
homeRobot.assertNavigatedToAuth() robot.assertNavigatedToAuth()
} }
/** GIVEN success response WHEN data is returned THEN it is shown on the ui */ /** GIVEN success response WHEN data is returned THEN it is shown on the ui */
@ -80,9 +69,9 @@ class MainActivityTest : KoinTest {
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
ContentData.contentSuccess.forEachIndexed { index, content -> ContentData.contentSuccess.forEachIndexed { index, content ->
homeRobot.assertContainsItem(index, FavouriteContent(content, false)) robot.assertContainsItem(index, FavouriteContent(content, false))
} }
homeRobot.assertDidNotNavigateToAuth() robot.assertDidNotNavigateToAuth()
} }
/** GIVEN success response WHEN item is clicked THEN ui is updated */ /** GIVEN success response WHEN item is clicked THEN ui is updated */
@ -92,11 +81,11 @@ class MainActivityTest : KoinTest {
activityScenario = ActivityScenario.launch(MainActivity::class.java) activityScenario = ActivityScenario.launch(MainActivity::class.java)
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
homeRobot.clickOnContentItem(0, ContentData.contentSuccess.first()) robot.clickOnContentItem(0, ContentData.contentSuccess.first())
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
val expectedItem = FavouriteContent(ContentData.contentSuccess.first(), true) val expectedItem = FavouriteContent(ContentData.contentSuccess.first(), true)
homeRobot.assertContainsItem(0, expectedItem) robot.assertContainsItem(0, expectedItem)
.assertDidNotNavigateToAuth() .assertDidNotNavigateToAuth()
} }
@ -107,7 +96,7 @@ class MainActivityTest : KoinTest {
activityScenario = ActivityScenario.launch(MainActivity::class.java) activityScenario = ActivityScenario.launch(MainActivity::class.java)
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
homeRobot.clickOnContentItem(0, ContentData.contentSuccess.first()) robot.clickOnContentItem(0, ContentData.contentSuccess.first())
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
val expectedItem = FavouriteContent(ContentData.contentSuccess.first(), true) val expectedItem = FavouriteContent(ContentData.contentSuccess.first(), true)
@ -116,7 +105,7 @@ class MainActivityTest : KoinTest {
activityScenario = ActivityScenario.launch(MainActivity::class.java) activityScenario = ActivityScenario.launch(MainActivity::class.java)
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
homeRobot.assertContainsItem(0, expectedItem) robot.assertContainsItem(0, expectedItem)
.assertDidNotNavigateToAuth() .assertDidNotNavigateToAuth()
} }
@ -127,13 +116,13 @@ class MainActivityTest : KoinTest {
activityScenario = ActivityScenario.launch(MainActivity::class.java) activityScenario = ActivityScenario.launch(MainActivity::class.java)
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
homeRobot.clickOnContentItem(0, ContentData.contentSuccess.first()) robot.clickOnContentItem(0, ContentData.contentSuccess.first())
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
homeRobot.clickOnContentItem(0, ContentData.contentSuccess.first()) robot.clickOnContentItem(0, ContentData.contentSuccess.first())
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
val expectedItem = FavouriteContent(ContentData.contentSuccess.first(), false) val expectedItem = FavouriteContent(ContentData.contentSuccess.first(), false)
homeRobot.assertContainsItem(0, expectedItem) robot.assertContainsItem(0, expectedItem)
.assertDidNotNavigateToAuth() .assertDidNotNavigateToAuth()
} }
@ -144,7 +133,7 @@ class MainActivityTest : KoinTest {
activityScenario = ActivityScenario.launch(MainActivity::class.java) activityScenario = ActivityScenario.launch(MainActivity::class.java)
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
homeRobot.assertContainsNoItems() robot.assertContainsNoItems()
.assertContainsError() .assertContainsError()
.assertDidNotNavigateToAuth() .assertDidNotNavigateToAuth()
} }
@ -159,14 +148,14 @@ class MainActivityTest : KoinTest {
activityScenario = ActivityScenario.launch(MainActivity::class.java) activityScenario = ActivityScenario.launch(MainActivity::class.java)
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
homeRobot.swipeRefresh() robot.swipeRefresh()
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
loopMainThreadFor(2000L) loopMainThreadFor(2000L)
ContentData.contentSuccess.forEachIndexed { index, content -> ContentData.contentSuccess.forEachIndexed { index, content ->
homeRobot.assertContainsItem(index, FavouriteContent(content, false)) robot.assertContainsItem(index, FavouriteContent(content, false))
} }
homeRobot.assertDidNotNavigateToAuth() robot.assertDidNotNavigateToAuth()
} }
/** GIVEN success then error WHEN retried THEN error is shown */ /** GIVEN success then error WHEN retried THEN error is shown */
@ -179,13 +168,13 @@ class MainActivityTest : KoinTest {
activityScenario = ActivityScenario.launch(MainActivity::class.java) activityScenario = ActivityScenario.launch(MainActivity::class.java)
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
homeRobot.swipeRefresh() robot.swipeRefresh()
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
loopMainThreadUntilIdleWithIdlingResources() loopMainThreadUntilIdleWithIdlingResources()
mainDispatcherTestRule.advanceTimeBy(1000L) mainDispatcherTestRule.advanceTimeBy(1000L)
loopMainThreadFor(1000) loopMainThreadFor(1000)
homeRobot robot
.assertContainsError() .assertContainsError()
.assertContainsNoItems() .assertContainsNoItems()
.assertDidNotNavigateToAuth() .assertDidNotNavigateToAuth()
@ -204,9 +193,9 @@ class MainActivityTest : KoinTest {
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
ContentData.contentSuccess.forEachIndexed { index, content -> ContentData.contentSuccess.forEachIndexed { index, content ->
homeRobot.assertContainsItem(index, FavouriteContent(content, false)) robot.assertContainsItem(index, FavouriteContent(content, false))
} }
homeRobot.assertDidNotNavigateToAuth() robot.assertDidNotNavigateToAuth()
} }
/** GIVEN unauthenticated then error WHEN loaded THEN navigated to auth */ /** GIVEN unauthenticated then error WHEN loaded THEN navigated to auth */
@ -218,6 +207,6 @@ class MainActivityTest : KoinTest {
activityScenario = ActivityScenario.launch(MainActivity::class.java) activityScenario = ActivityScenario.launch(MainActivity::class.java)
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
homeRobot.assertNavigatedToAuth() robot.assertNavigatedToAuth()
} }
} }

View file

@ -4,16 +4,14 @@ 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.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.MockServerScenarioSetupTestRule import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
import org.fnives.test.showcase.testutils.configuration.SpecificTestConfigurationsFactory import org.fnives.test.showcase.testutils.configuration.MainDispatcherTestRule
import org.fnives.test.showcase.testutils.idling.Disposable
import org.fnives.test.showcase.testutils.idling.NetworkSynchronization
import org.fnives.test.showcase.testutils.robot.RobotTestRule import org.fnives.test.showcase.testutils.robot.RobotTestRule
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.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.koin.test.KoinTest import org.koin.test.KoinTest
@ -23,31 +21,20 @@ class AuthActivityTest : KoinTest {
private lateinit var activityScenario: ActivityScenario<AuthActivity> private lateinit var activityScenario: ActivityScenario<AuthActivity>
@Rule private val mockServerScenarioSetupTestRule = MockServerScenarioSetupResetingTestRule()
@JvmField private val mockServerScenarioSetup get() = mockServerScenarioSetupTestRule.mockServerScenarioSetup
val robotRule = RobotTestRule(LoginRobot()) private val mainDispatcherTestRule = MainDispatcherTestRule()
private val loginRobot get() = robotRule.robot private val robot = LoginRobot()
@Rule @Rule
@JvmField @JvmField
val mockServerScenarioSetupTestRule = MockServerScenarioSetupTestRule() val ruleOrder: RuleChain = RuleChain.outerRule(mockServerScenarioSetupTestRule)
val mockServerScenarioSetup get() = mockServerScenarioSetupTestRule.mockServerScenarioSetup .around(mainDispatcherTestRule)
.around(RobotTestRule(robot))
@Rule
@JvmField
val mainDispatcherTestRule = SpecificTestConfigurationsFactory.createMainDispatcherTestRule()
private lateinit var disposable: Disposable
@Before
fun setUp() {
disposable = NetworkSynchronization.registerNetworkingSynchronization()
}
@After @After
fun tearDown() { fun tearDown() {
activityScenario.close() activityScenario.close()
disposable.dispose()
} }
/** 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 */
@ -57,7 +44,7 @@ class AuthActivityTest : KoinTest {
AuthScenario.Success(password = "alma", username = "banan") AuthScenario.Success(password = "alma", username = "banan")
) )
activityScenario = ActivityScenario.launch(AuthActivity::class.java) activityScenario = ActivityScenario.launch(AuthActivity::class.java)
loginRobot robot
.setPassword("alma") .setPassword("alma")
.setUsername("banan") .setUsername("banan")
.assertPassword("alma") .assertPassword("alma")
@ -66,21 +53,21 @@ class AuthActivityTest : KoinTest {
.assertLoadingBeforeRequests() .assertLoadingBeforeRequests()
mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed() mainDispatcherTestRule.advanceUntilIdleOrActivityIsDestroyed()
loginRobot.assertNavigatedToHome() robot.assertNavigatedToHome()
} }
/** GIVEN empty password and username WHEN signIn THEN error password is shown */ /** GIVEN empty password and username WHEN signIn THEN error password is shown */
@Test @Test
fun emptyPasswordShowsProperErrorMessage() { fun emptyPasswordShowsProperErrorMessage() {
activityScenario = ActivityScenario.launch(AuthActivity::class.java) activityScenario = ActivityScenario.launch(AuthActivity::class.java)
loginRobot robot
.setUsername("banan") .setUsername("banan")
.assertUsername("banan") .assertUsername("banan")
.clickOnLogin() .clickOnLogin()
.assertLoadingBeforeRequests() .assertLoadingBeforeRequests()
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
loginRobot.assertErrorIsShown(R.string.password_is_invalid) robot.assertErrorIsShown(R.string.password_is_invalid)
.assertNotNavigatedToHome() .assertNotNavigatedToHome()
.assertNotLoading() .assertNotLoading()
} }
@ -89,14 +76,14 @@ class AuthActivityTest : KoinTest {
@Test @Test
fun emptyUserNameShowsProperErrorMessage() { fun emptyUserNameShowsProperErrorMessage() {
activityScenario = ActivityScenario.launch(AuthActivity::class.java) activityScenario = ActivityScenario.launch(AuthActivity::class.java)
loginRobot robot
.setPassword("banan") .setPassword("banan")
.assertPassword("banan") .assertPassword("banan")
.clickOnLogin() .clickOnLogin()
.assertLoadingBeforeRequests() .assertLoadingBeforeRequests()
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
loginRobot.assertErrorIsShown(R.string.username_is_invalid) robot.assertErrorIsShown(R.string.username_is_invalid)
.assertNotNavigatedToHome() .assertNotNavigatedToHome()
.assertNotLoading() .assertNotLoading()
} }
@ -108,7 +95,7 @@ class AuthActivityTest : KoinTest {
AuthScenario.InvalidCredentials(username = "alma", password = "banan") AuthScenario.InvalidCredentials(username = "alma", password = "banan")
) )
activityScenario = ActivityScenario.launch(AuthActivity::class.java) activityScenario = ActivityScenario.launch(AuthActivity::class.java)
loginRobot robot
.setUsername("alma") .setUsername("alma")
.setPassword("banan") .setPassword("banan")
.assertUsername("alma") .assertUsername("alma")
@ -117,7 +104,7 @@ class AuthActivityTest : KoinTest {
.assertLoadingBeforeRequests() .assertLoadingBeforeRequests()
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
loginRobot.assertErrorIsShown(R.string.credentials_invalid) robot.assertErrorIsShown(R.string.credentials_invalid)
.assertNotNavigatedToHome() .assertNotNavigatedToHome()
.assertNotLoading() .assertNotLoading()
} }
@ -129,7 +116,7 @@ class AuthActivityTest : KoinTest {
AuthScenario.GenericError(username = "alma", password = "banan") AuthScenario.GenericError(username = "alma", password = "banan")
) )
activityScenario = ActivityScenario.launch(AuthActivity::class.java) activityScenario = ActivityScenario.launch(AuthActivity::class.java)
loginRobot robot
.setUsername("alma") .setUsername("alma")
.setPassword("banan") .setPassword("banan")
.assertUsername("alma") .assertUsername("alma")
@ -138,7 +125,7 @@ class AuthActivityTest : KoinTest {
.assertLoadingBeforeRequests() .assertLoadingBeforeRequests()
mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
loginRobot.assertErrorIsShown(R.string.something_went_wrong) robot.assertErrorIsShown(R.string.something_went_wrong)
.assertNotNavigatedToHome() .assertNotNavigatedToHome()
.assertNotLoading() .assertNotLoading()
} }

View file

@ -3,15 +3,15 @@ package org.fnives.test.showcase.ui.splash
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
import org.fnives.test.showcase.testutils.MockServerScenarioSetupTestRule import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
import org.fnives.test.showcase.testutils.configuration.SpecificTestConfigurationsFactory import org.fnives.test.showcase.testutils.configuration.MainDispatcherTestRule
import org.fnives.test.showcase.testutils.idling.Disposable
import org.fnives.test.showcase.testutils.idling.NetworkSynchronization
import org.fnives.test.showcase.testutils.robot.RobotTestRule import org.fnives.test.showcase.testutils.robot.RobotTestRule
import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogin
import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogout
import org.junit.After import org.junit.After
import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.koin.test.KoinTest import org.koin.test.KoinTest
@ -21,44 +21,33 @@ class SplashActivityTest : KoinTest {
private lateinit var activityScenario: ActivityScenario<SplashActivity> private lateinit var activityScenario: ActivityScenario<SplashActivity>
private val splashRobot: SplashRobot get() = robotTestRule.robot private val mainDispatcherTestRule = MainDispatcherTestRule()
private val mockServerScenarioSetupTestRule = MockServerScenarioSetupResetingTestRule()
private val robot = SplashRobot()
@Rule @Rule
@JvmField @JvmField
val robotTestRule = RobotTestRule(SplashRobot()) val ruleOrder: RuleChain = RuleChain.outerRule(mockServerScenarioSetupTestRule)
.around(mainDispatcherTestRule)
@Rule .around(RobotTestRule(robot))
@JvmField
val mainDispatcherTestRule = SpecificTestConfigurationsFactory.createMainDispatcherTestRule()
@Rule
@JvmField
val mockServerScenarioSetupTestRule = MockServerScenarioSetupTestRule()
lateinit var disposable: Disposable
@Before
fun setUp() {
disposable = NetworkSynchronization.registerNetworkingSynchronization()
}
@After @After
fun tearDown() { fun tearDown() {
activityScenario.close() activityScenario.close()
disposable.dispose()
} }
/** GIVEN loggedInState WHEN opened after some time THEN MainActivity is started */ /** GIVEN loggedInState WHEN opened after some time THEN MainActivity is started */
@Test @Test
fun loggedInStateNavigatesToHome() { fun loggedInStateNavigatesToHome() {
splashRobot.setupLoggedInState(mainDispatcherTestRule, mockServerScenarioSetupTestRule.mockServerScenarioSetup) setupLogin(mainDispatcherTestRule, mockServerScenarioSetupTestRule.mockServerScenarioSetup)
activityScenario = ActivityScenario.launch(SplashActivity::class.java) activityScenario = ActivityScenario.launch(SplashActivity::class.java)
activityScenario.moveToState(Lifecycle.State.RESUMED) activityScenario.moveToState(Lifecycle.State.RESUMED)
mainDispatcherTestRule.advanceTimeBy(501) mainDispatcherTestRule.advanceTimeBy(501)
splashRobot.assertHomeIsStarted() robot.assertHomeIsStarted()
.assertAuthIsNotStarted() .assertAuthIsNotStarted()
workaroundForActivityScenarioCLoseLockingUp() workaroundForActivityScenarioCLoseLockingUp()
@ -67,13 +56,13 @@ class SplashActivityTest : KoinTest {
/** GIVEN loggedOffState WHEN opened after some time THEN AuthActivity is started */ /** GIVEN loggedOffState WHEN opened after some time THEN AuthActivity is started */
@Test @Test
fun loggedOutStatesNavigatesToAuthentication() { fun loggedOutStatesNavigatesToAuthentication() {
splashRobot.setupLoggedOutState(mainDispatcherTestRule) setupLogout(mainDispatcherTestRule)
activityScenario = ActivityScenario.launch(SplashActivity::class.java) activityScenario = ActivityScenario.launch(SplashActivity::class.java)
activityScenario.moveToState(Lifecycle.State.RESUMED) activityScenario.moveToState(Lifecycle.State.RESUMED)
mainDispatcherTestRule.advanceTimeBy(501) mainDispatcherTestRule.advanceTimeBy(501)
splashRobot.assertAuthIsStarted() robot.assertAuthIsStarted()
.assertHomeIsNotStarted() .assertHomeIsNotStarted()
workaroundForActivityScenarioCLoseLockingUp() workaroundForActivityScenarioCLoseLockingUp()
@ -82,27 +71,27 @@ class SplashActivityTest : KoinTest {
/** 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 */
@Test @Test
fun loggedOutStatesNotEnoughTime() { fun loggedOutStatesNotEnoughTime() {
splashRobot.setupLoggedOutState(mainDispatcherTestRule) setupLogout(mainDispatcherTestRule)
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(10)
splashRobot.assertAuthIsNotStarted() robot.assertAuthIsNotStarted()
.assertHomeIsNotStarted() .assertHomeIsNotStarted()
} }
/** GIVEN loggedInState and not enough time WHEN opened THEN no activity is started */ /** GIVEN loggedInState and not enough time WHEN opened THEN no activity is started */
@Test @Test
fun loggedInStatesNotEnoughTime() { fun loggedInStatesNotEnoughTime() {
splashRobot.setupLoggedInState(mainDispatcherTestRule, mockServerScenarioSetupTestRule.mockServerScenarioSetup) setupLogin(mainDispatcherTestRule, mockServerScenarioSetupTestRule.mockServerScenarioSetup)
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(10)
splashRobot.assertHomeIsNotStarted() robot.assertHomeIsNotStarted()
.assertAuthIsNotStarted() .assertAuthIsNotStarted()
} }

View file

@ -1,12 +1,8 @@
package org.fnives.test.showcase.ui.splash package org.fnives.test.showcase.ui.splash
import android.app.Instrumentation
import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers import androidx.test.espresso.intent.matcher.IntentMatchers
import org.fnives.test.showcase.network.mockserver.MockServerScenarioSetup
import org.fnives.test.showcase.testutils.configuration.MainDispatcherTestRule
import org.fnives.test.showcase.testutils.robot.Robot import org.fnives.test.showcase.testutils.robot.Robot
import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState
import org.fnives.test.showcase.testutils.viewactions.notIntended import org.fnives.test.showcase.testutils.viewactions.notIntended
import org.fnives.test.showcase.ui.auth.AuthActivity import org.fnives.test.showcase.ui.auth.AuthActivity
import org.fnives.test.showcase.ui.home.MainActivity import org.fnives.test.showcase.ui.home.MainActivity
@ -15,33 +11,12 @@ class SplashRobot : Robot {
override fun init() { override fun init() {
Intents.init() Intents.init()
Intents.intending(IntentMatchers.hasComponent(MainActivity::class.java.canonicalName))
.respondWith(Instrumentation.ActivityResult(0, null))
Intents.intending(IntentMatchers.hasComponent(AuthActivity::class.java.canonicalName))
.respondWith(Instrumentation.ActivityResult(0, null))
} }
override fun release() { override fun release() {
Intents.release() Intents.release()
} }
fun setupLoggedInState(
mainDispatcherTestRule: MainDispatcherTestRule,
mockServerScenarioSetup: MockServerScenarioSetup
) {
SetupAuthenticationState.setupLogin(mainDispatcherTestRule, mockServerScenarioSetup)
release()
init()
}
fun setupLoggedOutState(
mainDispatcherTestRule: MainDispatcherTestRule
) {
SetupAuthenticationState.setupLogout(mainDispatcherTestRule)
release()
init()
}
fun assertHomeIsStarted() = apply { fun assertHomeIsStarted() = apply {
Intents.intended(IntentMatchers.hasComponent(MainActivity::class.java.canonicalName)) Intents.intended(IntentMatchers.hasComponent(MainActivity::class.java.canonicalName))
} }