diff --git a/robolectric-extension/src/main/kotlin/tech/apter/junit/jupiter/robolectric/internal/BlockJUnit4ClassRunnerExtensions.kt b/robolectric-extension/src/main/kotlin/tech/apter/junit/jupiter/robolectric/internal/BlockJUnit4ClassRunnerExtensions.kt index 3f1d764..d057213 100644 --- a/robolectric-extension/src/main/kotlin/tech/apter/junit/jupiter/robolectric/internal/BlockJUnit4ClassRunnerExtensions.kt +++ b/robolectric-extension/src/main/kotlin/tech/apter/junit/jupiter/robolectric/internal/BlockJUnit4ClassRunnerExtensions.kt @@ -8,10 +8,14 @@ import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.RepeatedTest import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestFactory +import org.junit.jupiter.api.TestTemplate import org.junit.runners.BlockJUnit4ClassRunner import org.junit.runners.model.FrameworkMethod +import org.junit.runners.model.TestClass +import java.lang.reflect.Modifier import java.util.Collections private val parameterizedTestAnnotation: Class? by lazy { @@ -24,19 +28,35 @@ private val parameterizedTestAnnotation: Class? by lazy { } internal fun BlockJUnit4ClassRunner.computeJUnit5TestMethods(): MutableList { - val testMethods = testClass.getAnnotatedMethods(Test::class.java) - val testFactoryMethods = testClass.getAnnotatedMethods(TestFactory::class.java) - val parameterizedTestMethods = if (parameterizedTestAnnotation == null) { - emptyList() - } else { - testClass.getAnnotatedMethods(parameterizedTestAnnotation) + fun testMethods(testClass: TestClass): MutableList { + val testMethods = testClass.getAnnotatedMethods(Test::class.java) + val testFactoryMethods = testClass.getAnnotatedMethods(TestFactory::class.java) + val repeatedTestMethods = testClass.getAnnotatedMethods(RepeatedTest::class.java) + val testTemplateMethods = testClass.getAnnotatedMethods(TestTemplate::class.java) + val parameterizedTestMethods = if (parameterizedTestAnnotation == null) { + emptyList() + } else { + testClass.getAnnotatedMethods(parameterizedTestAnnotation) + } + + val nestedTestMethods = testClass + .javaClass + .declaredClasses + .filter { !Modifier.isStatic(it.modifiers) } + .map { testMethods(TestClass(it)) } + .flatten() + + val methods = mutableListOf() + methods.addAll(testMethods) + methods.addAll(testTemplateMethods) + methods.addAll(testFactoryMethods) + methods.addAll(repeatedTestMethods) + methods.addAll(parameterizedTestMethods) + methods.addAll(nestedTestMethods) + return Collections.unmodifiableList(methods) } - val methods = mutableListOf() - methods.addAll(testMethods) - methods.addAll(testFactoryMethods) - methods.addAll(parameterizedTestMethods) - return Collections.unmodifiableList(methods) + return testMethods(testClass) } @Suppress("UnusedReceiverParameter") diff --git a/robolectric-extension/src/main/kotlin/tech/apter/junit/jupiter/robolectric/internal/JUnit5RobolectricTestRunner.kt b/robolectric-extension/src/main/kotlin/tech/apter/junit/jupiter/robolectric/internal/JUnit5RobolectricTestRunner.kt index aea02fb..85206a6 100644 --- a/robolectric-extension/src/main/kotlin/tech/apter/junit/jupiter/robolectric/internal/JUnit5RobolectricTestRunner.kt +++ b/robolectric-extension/src/main/kotlin/tech/apter/junit/jupiter/robolectric/internal/JUnit5RobolectricTestRunner.kt @@ -53,6 +53,10 @@ internal class JUnit5RobolectricTestRunner(clazz: Class<*>, injector: Injector = override fun computeTestMethods() = computeJUnit5TestMethods() + override fun validateNoNonStaticInnerClass(errors: MutableList) { + // Skip validation + } + override fun isIgnored(child: FrameworkMethod) = isJUnit5Ignored(child) override fun validatePublicVoidNoArgMethods( @@ -68,6 +72,10 @@ internal class JUnit5RobolectricTestRunner(clazz: Class<*>, injector: Injector = RobolectricTestRunner.HelperTestRunner(bootstrappedTestClass) { override fun computeTestMethods(): MutableList = computeJUnit5TestMethods() + override fun validateNoNonStaticInnerClass(errors: MutableList) { + // Skip validation + } + override fun isIgnored(child: FrameworkMethod) = isJUnit5Ignored(child) override fun validatePublicVoidNoArgMethods( diff --git a/robolectric-extension/src/test/kotlin/tech/apter/junit/jupiter/robolectric/RobolectricExtensionNestedSelfTest.kt b/robolectric-extension/src/test/kotlin/tech/apter/junit/jupiter/robolectric/RobolectricExtensionNestedSelfTest.kt new file mode 100644 index 0000000..58d878f --- /dev/null +++ b/robolectric-extension/src/test/kotlin/tech/apter/junit/jupiter/robolectric/RobolectricExtensionNestedSelfTest.kt @@ -0,0 +1,24 @@ +package tech.apter.junit.jupiter.robolectric + +import android.app.Application +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.extension.ExtendWith +import kotlin.test.Test +import kotlin.test.assertIs +import kotlin.test.assertNotNull + +@ExtendWith(RobolectricExtension::class) +class RobolectricExtensionNestedSelfTest { + @Nested + inner class NestedSelfTest { + @Test + fun `Given a test extended with robolectric when call a nested test then robolectric should be available`() { + val application = assertDoesNotThrow { ApplicationProvider.getApplicationContext() } + assertNotNull(application) + assertIs(application, "application") + } + } +} diff --git a/robolectric-extension/src/test/kotlin/tech/apter/junit/jupiter/robolectric/RobolectricExtensionRepeatedSelfTest.kt b/robolectric-extension/src/test/kotlin/tech/apter/junit/jupiter/robolectric/RobolectricExtensionRepeatedSelfTest.kt new file mode 100644 index 0000000..b3ed5f2 --- /dev/null +++ b/robolectric-extension/src/test/kotlin/tech/apter/junit/jupiter/robolectric/RobolectricExtensionRepeatedSelfTest.kt @@ -0,0 +1,40 @@ +package tech.apter.junit.jupiter.robolectric + +import android.app.Application +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.RepeatedTest +import org.junit.jupiter.api.RepetitionInfo +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.extension.ExtendWith +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertNotNull + +@ExtendWith(RobolectricExtension::class) +class RobolectricExtensionRepeatedSelfTest { + + @RepeatedTest(REPEATED_TEST_COUNT) + fun `Given a test extended with robolectric when call a repeated test then robolectric should be available`( + testInfo: RepetitionInfo, + ) { + testCallCount++ + val application = assertDoesNotThrow { ApplicationProvider.getApplicationContext() } + assertNotNull(application) + assertIs(application, "application") + assertEquals(REPEATED_TEST_COUNT, testInfo.totalRepetitions) + } + + companion object { + private const val REPEATED_TEST_COUNT = 3 + private var testCallCount: Int = 0 + + @AfterAll + @Throws(Exception::class) + @JvmStatic + fun `Repeated test should be called as much as REPEATED_TEST_COUNT`() { + assertEquals(REPEATED_TEST_COUNT, testCallCount) + } + } +} diff --git a/robolectric-extension/src/test/kotlin/tech/apter/junit/jupiter/robolectric/RobolectricExtensionTestTemplateSelfTest.kt b/robolectric-extension/src/test/kotlin/tech/apter/junit/jupiter/robolectric/RobolectricExtensionTestTemplateSelfTest.kt new file mode 100644 index 0000000..3521da6 --- /dev/null +++ b/robolectric-extension/src/test/kotlin/tech/apter/junit/jupiter/robolectric/RobolectricExtensionTestTemplateSelfTest.kt @@ -0,0 +1,51 @@ +package tech.apter.junit.jupiter.robolectric + +import android.app.Application +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.TestTemplate +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.TestTemplateInvocationContext +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider +import java.util.stream.Stream +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertNotNull + +@ExtendWith(RobolectricExtension::class) +class RobolectricExtensionTestTemplateSelfTest { + + @TestTemplate + @ExtendWith(RobolectricTestTemplateInvocationContextProvider::class) + fun `Given a test extended with robolectric when call a test template then robolectric should be available`() { + testCallCount++ + val application = assertDoesNotThrow { ApplicationProvider.getApplicationContext() } + assertNotNull(application) + assertIs(application, "application") + } + + companion object { + private var testCallCount: Int = 0 + + @AfterAll + @Throws(Exception::class) + @JvmStatic + fun `Test template should be called as much as defined RobolectricTestTemplateInvocationContextProvider`() { + assertEquals(2, testCallCount) + } + } +} + +private fun noOpTestTemplateInvocationContext(): TestTemplateInvocationContext = + object : TestTemplateInvocationContext {} + +private class RobolectricTestTemplateInvocationContextProvider : TestTemplateInvocationContextProvider { + override fun supportsTestTemplate(context: ExtensionContext): Boolean = true + + override fun provideTestTemplateInvocationContexts(context: ExtensionContext): Stream { + return Stream.of(noOpTestTemplateInvocationContext(), noOpTestTemplateInvocationContext()) + } +}