Issue#13 Simplify swapping of URL and certificate for tests

This commit is contained in:
Gergely Hegedus 2022-01-26 23:34:58 +02:00
parent 9bdcaddb0c
commit b278466095
13 changed files with 96 additions and 108 deletions

View file

@ -38,7 +38,7 @@ android {
sourceSets { sourceSets {
androidTest { androidTest {
java.srcDirs += "src/sharedTest/java" // java.srcDirs += "src/sharedTest/java"
assets.srcDirs += files("$projectDir/schemas".toString()) assets.srcDirs += files("$projectDir/schemas".toString())
} }
test { test {

View file

@ -1,29 +0,0 @@
package org.fnives.test.showcase.testutils.configuration
import okhttp3.OkHttpClient
import org.fnives.test.showcase.network.mockserver.MockServerScenarioSetup
import org.fnives.test.showcase.testutils.idling.NetworkSynchronization
import org.koin.core.context.loadKoinModules
import org.koin.core.qualifier.StringQualifier
import org.koin.dsl.module
import org.koin.test.KoinTest
import org.koin.test.get
object AndroidTestServerTypeConfiguration : ServerTypeConfiguration, KoinTest {
override val useHttps: Boolean get() = true
override val url: String get() = "${MockServerScenarioSetup.HTTPS_BASE_URL}:${MockServerScenarioSetup.PORT}/"
override fun invoke(mockServerScenarioSetup: MockServerScenarioSetup) {
val handshakeCertificates = mockServerScenarioSetup.clientCertificates ?: return
val sessionless = StringQualifier(NetworkSynchronization.OkHttpClientTypes.SESSIONLESS.qualifier)
val okHttpClientWithCertificate = get<OkHttpClient>(sessionless).newBuilder()
.sslSocketFactory(handshakeCertificates.sslSocketFactory(), handshakeCertificates.trustManager)
.build()
loadKoinModules(
module {
single(qualifier = sessionless) { okHttpClientWithCertificate }
}
)
}
}

View file

@ -4,9 +4,6 @@ object SpecificTestConfigurationsFactory : TestConfigurationsFactory {
override fun createMainDispatcherTestRule(): MainDispatcherTestRule = override fun createMainDispatcherTestRule(): MainDispatcherTestRule =
AndroidTestMainDispatcherTestRule() AndroidTestMainDispatcherTestRule()
override fun createServerTypeConfiguration(): ServerTypeConfiguration =
AndroidTestServerTypeConfiguration
override fun createLoginRobotConfiguration(): LoginRobotConfiguration = override fun createLoginRobotConfiguration(): LoginRobotConfiguration =
AndroidTestLoginRobotConfiguration AndroidTestLoginRobotConfiguration

View file

@ -1,11 +0,0 @@
package org.fnives.test.showcase.testutils.configuration
import org.fnives.test.showcase.network.mockserver.MockServerScenarioSetup
object RobolectricServerTypeConfiguration : ServerTypeConfiguration {
override val useHttps: Boolean = false
override val url: String get() = "${MockServerScenarioSetup.HTTP_BASE_URL}:${MockServerScenarioSetup.PORT}/"
override fun invoke(mockServerScenarioSetup: MockServerScenarioSetup) = Unit
}

View file

@ -4,9 +4,6 @@ object SpecificTestConfigurationsFactory : TestConfigurationsFactory {
override fun createMainDispatcherTestRule(): MainDispatcherTestRule = override fun createMainDispatcherTestRule(): MainDispatcherTestRule =
TestCoroutineMainDispatcherTestRule() TestCoroutineMainDispatcherTestRule()
override fun createServerTypeConfiguration(): ServerTypeConfiguration =
RobolectricServerTypeConfiguration
override fun createLoginRobotConfiguration(): LoginRobotConfiguration = override fun createLoginRobotConfiguration(): LoginRobotConfiguration =
RobolectricLoginRobotConfiguration RobolectricLoginRobotConfiguration

View file

@ -1,35 +1,63 @@
package org.fnives.test.showcase.testutils package org.fnives.test.showcase.testutils
import okhttp3.OkHttpClient
import okhttp3.tls.HandshakeCertificates
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.configuration.ServerTypeConfiguration import org.fnives.test.showcase.testutils.idling.NetworkSynchronization.OkHttpClientTypes
import org.fnives.test.showcase.testutils.configuration.SpecificTestConfigurationsFactory
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.dsl.module
import org.koin.test.KoinTest
import org.koin.test.get
class MockServerScenarioSetupTestRule : ReloadKoinModulesIfNecessaryTestRule(), KoinTest {
class MockServerScenarioSetupTestRule(
val serverTypeConfiguration: ServerTypeConfiguration = SpecificTestConfigurationsFactory.createServerTypeConfiguration()
) : TestRule {
lateinit var mockServerScenarioSetup: MockServerScenarioSetup lateinit var mockServerScenarioSetup: MockServerScenarioSetup
private val sessionlessQualifier get() = OkHttpClientTypes.SESSIONLESS.asQualifier()
override fun apply(base: Statement, description: Description): Statement = override fun apply(base: Statement, description: Description): Statement =
object : Statement() { super.apply(createStatement(base), description)
@Throws(Throwable::class)
override fun evaluate() { private fun createStatement(base: Statement) = object : Statement() {
before() @Throws(Throwable::class)
try { override fun evaluate() {
base.evaluate() before()
} finally { try {
after() base.evaluate()
} } finally {
after()
} }
} }
}
private fun before() { private fun before() {
mockServerScenarioSetup = MockServerScenarioSetup() mockServerScenarioSetup = MockServerScenarioSetup()
mockServerScenarioSetup.start(serverTypeConfiguration.useHttps) val url = mockServerScenarioSetup.start(true)
val handshakeCertificates = mockServerScenarioSetup.clientCertificates
?: throw IllegalStateException("ClientCertificate should be accessable")
val okHttpClientWithCertificate = createUpdateOkHttpClient(handshakeCertificates)
loadKoinModules(
module {
// add https certificate to okhttp
single(qualifier = sessionlessQualifier) { okHttpClientWithCertificate }
// replace base url with mockWebServer's
single { BaseUrl(url) }
}
)
} }
private fun createUpdateOkHttpClient(handshakeCertificates: HandshakeCertificates) =
get<OkHttpClient>(sessionlessQualifier).newBuilder()
.sslSocketFactory(handshakeCertificates.sslSocketFactory(), handshakeCertificates.trustManager)
.build()
private fun after() { private fun after() {
mockServerScenarioSetup.stop() mockServerScenarioSetup.stop()
} }

View file

@ -1,28 +1,50 @@
package org.fnives.test.showcase.testutils package org.fnives.test.showcase.testutils
import androidx.test.core.app.ApplicationProvider
import org.fnives.test.showcase.BuildConfig
import org.fnives.test.showcase.TestShowcaseApplication
import org.fnives.test.showcase.di.createAppModules
import org.fnives.test.showcase.model.network.BaseUrl
import org.junit.rules.TestRule 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.core.context.startKoin
import org.koin.core.context.stopKoin import org.koin.core.context.stopKoin
import org.koin.mp.KoinPlatformTools
import org.koin.test.KoinTest
class ReloadKoinModulesIfNecessaryTestRule : TestRule { /**
* Test rule to help reinitialize the whole Koin setup.
*
* It's needed because in AndroidTest's the Application is only called once,
* meaning our koin would be shared.
*
* 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 {
override fun apply(base: Statement, description: Description): Statement = override fun apply(base: Statement, description: Description): Statement =
object : Statement() { ReinitKoinStatement(base)
override fun evaluate() {
// TODO class ReinitKoinStatement(private val base: Statement) : Statement() {
// if (GlobalContext.getOrNull() == null) { override fun evaluate() {
// val application = reinitKoinIfNeeded()
// ApplicationProvider.getApplicationContext<TestShowcaseApplication>() try {
// startKoin { base.evaluate()
// androidContext(application) } finally {
// modules(createAppModules(BaseUrlProvider.get())) stopKoin()
// }
// }
try {
base.evaluate()
} finally {
stopKoin()
}
} }
} }
private fun reinitKoinIfNeeded() {
if (KoinPlatformTools.defaultContext().getOrNull() != null) return
val application = ApplicationProvider.getApplicationContext<TestShowcaseApplication>()
val baseUrl = BaseUrl(BuildConfig.BASE_URL)
startKoin {
androidContext(application)
modules(createAppModules(baseUrl))
}
}
}
} }

View file

@ -10,8 +10,6 @@ interface TestConfigurationsFactory {
fun createMainDispatcherTestRule(): MainDispatcherTestRule fun createMainDispatcherTestRule(): MainDispatcherTestRule
fun createServerTypeConfiguration(): ServerTypeConfiguration
fun createLoginRobotConfiguration(): LoginRobotConfiguration fun createLoginRobotConfiguration(): LoginRobotConfiguration
fun createSnackbarVerification(): SnackbarVerificationTestRule fun createSnackbarVerification(): SnackbarVerificationTestRule

View file

@ -21,12 +21,14 @@ object NetworkSynchronization : KoinTest {
return CompositeDisposable(idlingResources) return CompositeDisposable(idlingResources)
} }
private fun getOkHttpClient(type: OkHttpClientTypes): OkHttpClient = get(StringQualifier(type.qualifier)) private fun getOkHttpClient(type: OkHttpClientTypes): OkHttpClient = get(type.asQualifier())
private fun OkHttpClient.asIdlingResource(name: String): IdlingResource = private fun OkHttpClient.asIdlingResource(name: String): IdlingResource =
OkHttp3IdlingResource.create(name, this) OkHttp3IdlingResource.create(name, this)
enum class OkHttpClientTypes(val qualifier: String) { enum class OkHttpClientTypes(val qualifier: String) {
SESSION("SESSION-NETWORKING"), SESSIONLESS("SESSIONLESS-NETWORKING") SESSION("SESSION-NETWORKING"), SESSIONLESS("SESSIONLESS-NETWORKING");
fun asQualifier() = StringQualifier(qualifier)
} }
} }

View file

@ -8,7 +8,6 @@ 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.MockServerScenarioSetupTestRule
import org.fnives.test.showcase.testutils.ReloadKoinModulesIfNecessaryTestRule
import org.fnives.test.showcase.testutils.configuration.SpecificTestConfigurationsFactory import org.fnives.test.showcase.testutils.configuration.SpecificTestConfigurationsFactory
import org.fnives.test.showcase.testutils.idling.Disposable import org.fnives.test.showcase.testutils.idling.Disposable
import org.fnives.test.showcase.testutils.idling.NetworkSynchronization import org.fnives.test.showcase.testutils.idling.NetworkSynchronization
@ -52,16 +51,10 @@ class MainActivityTest : KoinTest {
@JvmField @JvmField
val mainDispatcherTestRule = SpecificTestConfigurationsFactory.createMainDispatcherTestRule() val mainDispatcherTestRule = SpecificTestConfigurationsFactory.createMainDispatcherTestRule()
@Rule
@JvmField
val reloadKoinModulesIfNecessaryTestRule = ReloadKoinModulesIfNecessaryTestRule()
private lateinit var disposable: Disposable private lateinit var disposable: Disposable
@Before @Before
fun setUp() { fun setUp() {
SpecificTestConfigurationsFactory.createServerTypeConfiguration()
.invoke(mockServerScenarioSetup)
disposable = NetworkSynchronization.registerNetworkingSynchronization() disposable = NetworkSynchronization.registerNetworkingSynchronization()
homeRobot.setupLogin( homeRobot.setupLogin(

View file

@ -6,7 +6,6 @@ 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.MockServerScenarioSetupTestRule
import org.fnives.test.showcase.testutils.ReloadKoinModulesIfNecessaryTestRule
import org.fnives.test.showcase.testutils.configuration.SpecificTestConfigurationsFactory import org.fnives.test.showcase.testutils.configuration.SpecificTestConfigurationsFactory
import org.fnives.test.showcase.testutils.idling.Disposable import org.fnives.test.showcase.testutils.idling.Disposable
import org.fnives.test.showcase.testutils.idling.NetworkSynchronization import org.fnives.test.showcase.testutils.idling.NetworkSynchronization
@ -47,16 +46,10 @@ class AuthActivityTest : KoinTest {
@JvmField @JvmField
val mainDispatcherTestRule = SpecificTestConfigurationsFactory.createMainDispatcherTestRule() val mainDispatcherTestRule = SpecificTestConfigurationsFactory.createMainDispatcherTestRule()
@Rule
@JvmField
val reloadKoinModulesIfNecessaryTestRule = ReloadKoinModulesIfNecessaryTestRule()
private lateinit var disposable: Disposable private lateinit var disposable: Disposable
@Before @Before
fun setUp() { fun setUp() {
SpecificTestConfigurationsFactory.createServerTypeConfiguration()
.invoke(mockServerScenarioSetup)
disposable = NetworkSynchronization.registerNetworkingSynchronization() disposable = NetworkSynchronization.registerNetworkingSynchronization()
} }

View file

@ -4,7 +4,6 @@ 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.MockServerScenarioSetupTestRule
import org.fnives.test.showcase.testutils.ReloadKoinModulesIfNecessaryTestRule
import org.fnives.test.showcase.testutils.configuration.SpecificTestConfigurationsFactory import org.fnives.test.showcase.testutils.configuration.SpecificTestConfigurationsFactory
import org.fnives.test.showcase.testutils.idling.Disposable import org.fnives.test.showcase.testutils.idling.Disposable
import org.fnives.test.showcase.testutils.idling.NetworkSynchronization import org.fnives.test.showcase.testutils.idling.NetworkSynchronization
@ -36,16 +35,10 @@ class SplashActivityTest : KoinTest {
@JvmField @JvmField
val mockServerScenarioSetupTestRule = MockServerScenarioSetupTestRule() val mockServerScenarioSetupTestRule = MockServerScenarioSetupTestRule()
@Rule
@JvmField
val reloadKoinModulesIfNecessaryTestRule = ReloadKoinModulesIfNecessaryTestRule()
lateinit var disposable: Disposable lateinit var disposable: Disposable
@Before @Before
fun setUp() { fun setUp() {
SpecificTestConfigurationsFactory.createServerTypeConfiguration()
.invoke(mockServerScenarioSetupTestRule.mockServerScenarioSetup)
disposable = NetworkSynchronization.registerNetworkingSynchronization() disposable = NetworkSynchronization.registerNetworkingSynchronization()
} }

View file

@ -29,12 +29,17 @@ fun createNetworkModules(
networkSessionExpirationListenerProvider: Scope.() -> NetworkSessionExpirationListener networkSessionExpirationListenerProvider: Scope.() -> NetworkSessionExpirationListener
): Sequence<Module> = ): Sequence<Module> =
sequenceOf( sequenceOf(
baseUrlModule(baseUrl),
loginModule(), loginModule(),
contentModule(), contentModule(),
sessionlessNetworkingModule(baseUrl, enableLogging), sessionlessNetworkingModule(enableLogging),
sessionNetworkingModule(networkSessionLocalStorageProvider, networkSessionExpirationListenerProvider) sessionNetworkingModule(networkSessionLocalStorageProvider, networkSessionExpirationListenerProvider)
) )
private fun baseUrlModule(baseUrl: BaseUrl) = module {
single { baseUrl }
}
private fun loginModule() = module { private fun loginModule() = module {
factory { LoginRemoteSourceImpl(get(), get()) } factory { LoginRemoteSourceImpl(get(), get()) }
factory<LoginRemoteSource> { get<LoginRemoteSourceImpl>() } factory<LoginRemoteSource> { get<LoginRemoteSourceImpl>() }
@ -48,7 +53,7 @@ private fun contentModule() = module {
factory<ContentRemoteSource> { get<ContentRemoteSourceImpl>() } factory<ContentRemoteSource> { get<ContentRemoteSourceImpl>() }
} }
private fun sessionlessNetworkingModule(baseUrl: BaseUrl, enableLogging: Boolean) = module { private fun sessionlessNetworkingModule(enableLogging: Boolean) = module {
factory { MoshiConverterFactory.create() } factory { MoshiConverterFactory.create() }
single(qualifier = sessionless) { single(qualifier = sessionless) {
OkHttpClient.Builder() OkHttpClient.Builder()
@ -58,7 +63,7 @@ private fun sessionlessNetworkingModule(baseUrl: BaseUrl, enableLogging: Boolean
} }
single(qualifier = sessionless) { single(qualifier = sessionless) {
Retrofit.Builder() Retrofit.Builder()
.baseUrl(baseUrl.baseUrl) .baseUrl(get<BaseUrl>().baseUrl)
.addConverterFactory(get<MoshiConverterFactory>()) .addConverterFactory(get<MoshiConverterFactory>())
.client(get(sessionless)) .client(get(sessionless))
.build() .build()