From b73ec0fef8ecc5af06ad5744c1f37e2a2a35ce1c Mon Sep 17 00:00:00 2001 From: SeungHun Choe <13363349+uOOOO@users.noreply.github.com> Date: Mon, 3 Jun 2024 23:45:08 +0900 Subject: [PATCH] Support nested test style spec (#13) --- .../build.gradle.kts | 2 +- .../extensions/ContainedRobolectricTest.kt | 18 +++++++++++ .../robolectric/RobolectricExtension.kt | 32 +++++++++++++------ 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/kotest-extensions-android/kotest-extensions-android-tests/build.gradle.kts b/kotest-extensions-android/kotest-extensions-android-tests/build.gradle.kts index 8ec2c38..07b7950 100644 --- a/kotest-extensions-android/kotest-extensions-android-tests/build.gradle.kts +++ b/kotest-extensions-android/kotest-extensions-android-tests/build.gradle.kts @@ -39,7 +39,7 @@ android { dependencies { implementation("androidx.test:core-ktx:1.5.0") testImplementation(project(":kotest-extensions-android")) - testImplementation("io.kotest:kotest-runner-junit5:5.8.0") + testImplementation("io.kotest:kotest-runner-junit5:5.9.0") androidTestImplementation("androidx.test:runner:1.5.2") androidTestImplementation("androidx.test:core:1.5.0") androidTestImplementation("androidx.test:rules:1.5.0") diff --git a/kotest-extensions-android/kotest-extensions-android-tests/src/test/kotlin/br/com/colman/kotest/android/extensions/ContainedRobolectricTest.kt b/kotest-extensions-android/kotest-extensions-android-tests/src/test/kotlin/br/com/colman/kotest/android/extensions/ContainedRobolectricTest.kt index 5fe6f62..02f5d8e 100644 --- a/kotest-extensions-android/kotest-extensions-android-tests/src/test/kotlin/br/com/colman/kotest/android/extensions/ContainedRobolectricTest.kt +++ b/kotest-extensions-android/kotest-extensions-android-tests/src/test/kotlin/br/com/colman/kotest/android/extensions/ContainedRobolectricTest.kt @@ -5,6 +5,7 @@ import android.os.Build import androidx.test.core.app.ApplicationProvider import br.com.colman.kotest.TestApplication import br.com.colman.kotest.android.extensions.robolectric.RobolectricTest +import io.kotest.core.spec.style.BehaviorSpec import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe @@ -65,3 +66,20 @@ abstract class ContainedRobolectricRunnerMergeOverwriteTest : StringSpec({ @Suppress("ClassName") @RobolectricTest(sdk = Build.VERSION_CODES.O_MR1) class ContainedRobolectricRunnerMergeOverwriteO_MR1Test : ContainedRobolectricRunnerMergeOverwriteTest() + +@RobolectricTest +class ContainedRobolectricRunnerDefaultApplicationBehaviorSpecTest : BehaviorSpec({ + Context("Get the Application defined in AndroidManifest.xml") { + Given("A application context") { + val applicationContext = ApplicationProvider.getApplicationContext() + + When("Get class from application context") { + val applicationClass = applicationContext::class + + Then("It should be TestApplication") { + applicationClass shouldBe TestApplication::class + } + } + } + } +}) diff --git a/kotest-extensions-android/src/main/kotlin/br/com/colman/kotest/android/extensions/robolectric/RobolectricExtension.kt b/kotest-extensions-android/src/main/kotlin/br/com/colman/kotest/android/extensions/robolectric/RobolectricExtension.kt index 4a056a1..51fa105 100644 --- a/kotest-extensions-android/src/main/kotlin/br/com/colman/kotest/android/extensions/robolectric/RobolectricExtension.kt +++ b/kotest-extensions-android/src/main/kotlin/br/com/colman/kotest/android/extensions/robolectric/RobolectricExtension.kt @@ -1,19 +1,22 @@ package br.com.colman.kotest.android.extensions.robolectric import android.app.Application -import io.kotest.common.runBlocking import io.kotest.core.extensions.ConstructorExtension import io.kotest.core.extensions.TestCaseExtension import io.kotest.core.spec.AutoScan import io.kotest.core.spec.Spec import io.kotest.core.test.TestCase import io.kotest.core.test.TestResult +import io.kotest.core.test.isRootTest +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.withContext import org.robolectric.annotation.Config -import java.util.concurrent.Callable +import org.robolectric.internal.bytecode.Sandbox import kotlin.reflect.KClass import kotlin.reflect.full.findAnnotation import kotlin.time.Duration import java.util.WeakHashMap +import java.util.concurrent.ExecutorService /** * We override TestCaseExtension to configure the Robolectric environment because TestCase intercept @@ -110,19 +113,30 @@ class RobolectricExtension : ConstructorExtension, TestCaseExtension { execute: suspend (TestCase) -> TestResult, ): TestResult { val containedRobolectricRunner = runnerMap[testCase.spec]!! - // sdkEnvironment.runOnMainThread is important to ensure Robolectric's + // Using sdkEnvironment.executorService to ensure Robolectric's // looper state doesn't carry over to the next test class. - return containedRobolectricRunner.sdkEnvironment.runOnMainThread( - Callable { + val executorService = containedRobolectricRunner.sdkEnvironment.getExecutorService() + return withContext(executorService.asCoroutineDispatcher()) { + if (testCase.isRootTest()) { containedRobolectricRunner.containedBefore() - val result = runBlocking { execute(testCase) } + } + val result = execute(testCase) + if (testCase.isRootTest()) { containedRobolectricRunner.containedAfter() - result - }, - ) + } + result + } } } +private fun Sandbox.getExecutorService(): ExecutorService { + val field = Sandbox::class.java.declaredFields + .firstOrNull { it.type == ExecutorService::class.java } + checkNotNull(field) { "Not found ExecutorService field." } + field.isAccessible = true + return field.get(this) as ExecutorService +} + internal class KotestDefaultApplication : Application() annotation class RobolectricTest(