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 {
androidTest {
java.srcDirs += "src/sharedTest/java"
// java.srcDirs += "src/sharedTest/java"
assets.srcDirs += files("$projectDir/schemas".toString())
}
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 =
AndroidTestMainDispatcherTestRule()
override fun createServerTypeConfiguration(): ServerTypeConfiguration =
AndroidTestServerTypeConfiguration
override fun createLoginRobotConfiguration(): LoginRobotConfiguration =
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 =
TestCoroutineMainDispatcherTestRule()
override fun createServerTypeConfiguration(): ServerTypeConfiguration =
RobolectricServerTypeConfiguration
override fun createLoginRobotConfiguration(): LoginRobotConfiguration =
RobolectricLoginRobotConfiguration

View file

@ -1,35 +1,63 @@
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.testutils.configuration.ServerTypeConfiguration
import org.fnives.test.showcase.testutils.configuration.SpecificTestConfigurationsFactory
import org.junit.rules.TestRule
import org.fnives.test.showcase.testutils.idling.NetworkSynchronization.OkHttpClientTypes
import org.junit.runner.Description
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
private val sessionlessQualifier get() = OkHttpClientTypes.SESSIONLESS.asQualifier()
override fun apply(base: Statement, description: Description): Statement =
object : Statement() {
@Throws(Throwable::class)
override fun evaluate() {
before()
try {
base.evaluate()
} finally {
after()
}
super.apply(createStatement(base), description)
private fun createStatement(base: Statement) = object : Statement() {
@Throws(Throwable::class)
override fun evaluate() {
before()
try {
base.evaluate()
} finally {
after()
}
}
}
private fun before() {
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() {
mockServerScenarioSetup.stop()
}

View file

@ -1,28 +1,50 @@
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.runner.Description
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.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 =
object : Statement() {
override fun evaluate() {
// TODO
// if (GlobalContext.getOrNull() == null) {
// val application =
// ApplicationProvider.getApplicationContext<TestShowcaseApplication>()
// startKoin {
// androidContext(application)
// modules(createAppModules(BaseUrlProvider.get()))
// }
// }
try {
base.evaluate()
} finally {
stopKoin()
}
ReinitKoinStatement(base)
class ReinitKoinStatement(private val base: Statement) : Statement() {
override fun evaluate() {
reinitKoinIfNeeded()
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 createServerTypeConfiguration(): ServerTypeConfiguration
fun createLoginRobotConfiguration(): LoginRobotConfiguration
fun createSnackbarVerification(): SnackbarVerificationTestRule

View file

@ -21,12 +21,14 @@ object NetworkSynchronization : KoinTest {
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 =
OkHttp3IdlingResource.create(name, this)
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.refresh.RefreshTokenScenario
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.idling.Disposable
import org.fnives.test.showcase.testutils.idling.NetworkSynchronization
@ -52,16 +51,10 @@ class MainActivityTest : KoinTest {
@JvmField
val mainDispatcherTestRule = SpecificTestConfigurationsFactory.createMainDispatcherTestRule()
@Rule
@JvmField
val reloadKoinModulesIfNecessaryTestRule = ReloadKoinModulesIfNecessaryTestRule()
private lateinit var disposable: Disposable
@Before
fun setUp() {
SpecificTestConfigurationsFactory.createServerTypeConfiguration()
.invoke(mockServerScenarioSetup)
disposable = NetworkSynchronization.registerNetworkingSynchronization()
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.network.mockserver.scenario.auth.AuthScenario
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.idling.Disposable
import org.fnives.test.showcase.testutils.idling.NetworkSynchronization
@ -47,16 +46,10 @@ class AuthActivityTest : KoinTest {
@JvmField
val mainDispatcherTestRule = SpecificTestConfigurationsFactory.createMainDispatcherTestRule()
@Rule
@JvmField
val reloadKoinModulesIfNecessaryTestRule = ReloadKoinModulesIfNecessaryTestRule()
private lateinit var disposable: Disposable
@Before
fun setUp() {
SpecificTestConfigurationsFactory.createServerTypeConfiguration()
.invoke(mockServerScenarioSetup)
disposable = NetworkSynchronization.registerNetworkingSynchronization()
}

View file

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

View file

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