diff --git a/build-logic/src/main/kotlin/Dependencies.kt b/build-logic/src/main/kotlin/Dependencies.kt index cec34331..706aeb8f 100644 --- a/build-logic/src/main/kotlin/Dependencies.kt +++ b/build-logic/src/main/kotlin/Dependencies.kt @@ -3,9 +3,9 @@ object libs { object versions { const val kotlin = "1.9.23" - const val junitJupiter = "5.10.2" - const val junitVintage = "5.10.2" - const val junitPlatform = "1.10.2" + const val junitJupiter = "5.11.0-M1" + const val junitVintage = "5.11.0-M1" + const val junitPlatform = "1.11.0-M1" const val composeBom = "2024.04.00" const val androidXTest = "1.5.0" diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/extensions/ClassExt.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/extensions/ClassExt.kt index fc077cfa..7615af31 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/extensions/ClassExt.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/extensions/ClassExt.kt @@ -2,21 +2,18 @@ package de.mannodermaus.junit5.internal.extensions import android.util.Log import de.mannodermaus.junit5.internal.LOG_TAG +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.jupiter.params.ParameterizedTest import java.lang.reflect.Method import java.lang.reflect.Modifier -private val jupiterTestAnnotations = listOf( - "org.junit.jupiter.api.Test", - "org.junit.jupiter.api.TestFactory", - "org.junit.jupiter.api.RepeatedTest", - "org.junit.jupiter.api.TestTemplate", - "org.junit.jupiter.params.ParameterizedTest", -) - -internal fun Class<*>.jupiterTestMethods(): Set = - jupiterTestMethods(includeInherited = true) +internal fun Class<*>.findJupiterTestMethods(): Set = + findJupiterTestMethods(includeInherited = true) -private fun Class<*>.jupiterTestMethods(includeInherited: Boolean): Set = buildSet { +private fun Class<*>.findJupiterTestMethods(includeInherited: Boolean): Set = buildSet { try { // Check each method in the Class for the presence // of the well-known list of JUnit Jupiter annotations. @@ -24,27 +21,34 @@ private fun Class<*>.jupiterTestMethods(includeInherited: Boolean): Set // Recursively check inner classes as well declaredClasses.forEach { inner -> - addAll(inner.jupiterTestMethods(includeInherited = false)) + addAll(inner.findJupiterTestMethods(includeInherited = false)) } // Attach methods from inherited superclass or (for Java) implemented interfaces, too if (includeInherited) { - addAll(superclass?.jupiterTestMethods(includeInherited = true).orEmpty()) - interfaces.forEach { i -> addAll(i.jupiterTestMethods(includeInherited = true)) } + addAll(superclass?.findJupiterTestMethods(includeInherited = true).orEmpty()) + interfaces.forEach { i -> addAll(i.findJupiterTestMethods(includeInherited = true)) } } } catch (t: Throwable) { Log.w(LOG_TAG, "${t.javaClass.name} in 'hasJupiterTestMethods()' for $name", t) } } +private val jupiterTestAnnotations = listOf( + Test::class.java, + TestFactory::class.java, + RepeatedTest::class.java, + TestTemplate::class.java, + ParameterizedTest::class.java, +) + private fun Array.filterAnnotatedByJUnitJupiter(): List = filter { method -> // The method must not be static... if (method.isStatic) return@filter false // ...and have at least one of the recognized JUnit 5 annotations - val names = method.declaredAnnotations.map { it.annotationClass.qualifiedName } - jupiterTestAnnotations.any(names::contains) + jupiterTestAnnotations.any { method.getAnnotation(it) != null } } private val Method.isStatic get() = Modifier.isStatic(modifiers) diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/JUnit5RunnerFactory.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/JUnit5RunnerFactory.kt index 0c0dae87..cb72abfd 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/JUnit5RunnerFactory.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/JUnit5RunnerFactory.kt @@ -1,7 +1,7 @@ package de.mannodermaus.junit5.internal.runners import android.os.Build -import de.mannodermaus.junit5.internal.extensions.jupiterTestMethods +import de.mannodermaus.junit5.internal.extensions.findJupiterTestMethods import org.junit.runner.Runner /** @@ -12,7 +12,7 @@ import org.junit.runner.Runner * which will highlight these tests as ignored. */ internal fun tryCreateJUnit5Runner(klass: Class<*>): Runner? { - val testMethods = klass.jupiterTestMethods() + val testMethods = klass.findJupiterTestMethods() if (testMethods.isEmpty()) { return null diff --git a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/AndroidJUnit5BuilderTests.kt b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/AndroidJUnit5BuilderTests.kt index 873bef22..407293d3 100644 --- a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/AndroidJUnit5BuilderTests.kt +++ b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/AndroidJUnit5BuilderTests.kt @@ -33,6 +33,7 @@ class AndroidJUnit5BuilderTests { HasTaggedTest::class.java, HasInheritedTestsFromClass::class.java, HasInheritedTestsFromInterface::class.java, + HasMultipleInheritancesAndOverrides::class.java, ) @TestFactory diff --git a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/TestClasses.kt b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/TestClasses.kt index eb376355..f8155ab0 100644 --- a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/TestClasses.kt +++ b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/TestClasses.kt @@ -85,7 +85,7 @@ class HasTaggedTest { abstract class AbstractTestClass { @Test - fun abstractTest() { + open fun abstractTest() { } } @@ -103,6 +103,16 @@ class HasInheritedTestsFromClass : AbstractTestClass() { class HasInheritedTestsFromInterface : AbstractTestInterface +class HasMultipleInheritancesAndOverrides : AbstractTestClass(), AbstractTestInterface { + @Test + override fun abstractTest() { + } + + @Test + fun someOtherTest() { + } +} + // These tests should not be acknowledged, // as classes with legacy tests & top-level tests // are unsupported by JUnit 5 diff --git a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/ExtensionsTests.kt b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/ExtensionsTests.kt index e7e86c6d..ae57ddd9 100644 --- a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/ExtensionsTests.kt +++ b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/ExtensionsTests.kt @@ -8,13 +8,14 @@ import de.mannodermaus.junit5.DoesntHaveTestMethods import de.mannodermaus.junit5.HasInheritedTestsFromClass import de.mannodermaus.junit5.HasInheritedTestsFromInterface import de.mannodermaus.junit5.HasInnerClassWithTest +import de.mannodermaus.junit5.HasMultipleInheritancesAndOverrides import de.mannodermaus.junit5.HasParameterizedTest import de.mannodermaus.junit5.HasRepeatedTest import de.mannodermaus.junit5.HasTaggedTest import de.mannodermaus.junit5.HasTest import de.mannodermaus.junit5.HasTestFactory import de.mannodermaus.junit5.HasTestTemplate -import de.mannodermaus.junit5.internal.extensions.jupiterTestMethods +import de.mannodermaus.junit5.internal.extensions.findJupiterTestMethods import de.mannodermaus.junit5.internal.runners.AndroidJUnit5 import de.mannodermaus.junit5.internal.runners.AndroidJUnit5RunnerParams import org.junit.jupiter.api.DisplayName @@ -34,7 +35,7 @@ class ExtensionsTests { @MethodSource("jupiterTestMethods") @DisplayName("jupiterTestMethods() has correct values & will execute expected number of tests") fun run(klass: Class<*>, expectExecutedTests: Int) { - val methods = klass.jupiterTestMethods() + val methods = klass.findJupiterTestMethods() if (expectExecutedTests == 0) { assertThat(methods).isEmpty() } else { @@ -60,7 +61,7 @@ class ExtensionsTests { @Test fun `tag filter works`() { val klass = HasTaggedTest::class.java - val methods = klass.jupiterTestMethods() + val methods = klass.findJupiterTestMethods() assertThat(methods).hasSize(1) // Verify number of executed test cases as well @@ -92,6 +93,7 @@ class ExtensionsTests { Arguments.of(HasInnerClassWithTest::class.java, 1), Arguments.of(HasInheritedTestsFromClass::class.java, 2), Arguments.of(HasInheritedTestsFromInterface::class.java, 1), + Arguments.of(HasMultipleInheritancesAndOverrides::class.java, 3) ) } } diff --git a/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/ActivityOneTest.kt b/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/ActivityOneTest.kt index e928a877..86bf8c4d 100644 --- a/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/ActivityOneTest.kt +++ b/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/ActivityOneTest.kt @@ -18,9 +18,15 @@ import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.FieldSource import org.junit.jupiter.params.provider.ValueSource +import java.util.function.Supplier +import java.util.stream.Stream class ActivityOneTest { + companion object { + val someLettersOfTheAlphabet = Supplier { Stream.of("A", "B", "C") } + } @JvmField @RegisterExtension @@ -62,6 +68,20 @@ class ActivityOneTest { } } + @FieldSource("someLettersOfTheAlphabet") + @ParameterizedTest + fun parameterizedTestWithFieldSource(letter: String) { + scenarioExtension.scenario.onActivity { + it.setButtonLabel(letter) + } + + onView(withText(letter)).perform(click()) + + scenarioExtension.scenario.onActivity { + assertEquals(1, it.getClickCount()) + } + } + @RepeatedTest(3) fun repeatedTestExample(repetitionInfo: RepetitionInfo, scenario: ActivityScenario) { val count = repetitionInfo.currentRepetition diff --git a/instrumentation/sample/src/test/kotlin/de/mannodermaus/sample/ExampleKotlinTest.kt b/instrumentation/sample/src/test/kotlin/de/mannodermaus/sample/ExampleKotlinTest.kt index 093ca19b..54875458 100644 --- a/instrumentation/sample/src/test/kotlin/de/mannodermaus/sample/ExampleKotlinTest.kt +++ b/instrumentation/sample/src/test/kotlin/de/mannodermaus/sample/ExampleKotlinTest.kt @@ -4,6 +4,7 @@ import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertAll import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeAll @@ -19,6 +20,7 @@ import org.junit.jupiter.api.TestFactory import org.junit.jupiter.api.TestInfo import org.junit.jupiter.api.function.Executable import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.FieldSource import org.junit.jupiter.params.provider.MethodSource import org.junit.jupiter.params.provider.ValueSource @@ -51,6 +53,8 @@ class ExampleKotlinTest { @JvmStatic fun getNames() = listOf("Alice" to "ALICE", "Bob" to "BOB", "Carol" to "CAROL") + + val somePrimeNumbers = intArrayOf(2, 3, 5, 7, 11, 13, 17, 19, 23, 29) } @BeforeEach @@ -107,10 +111,18 @@ class ExampleKotlinTest { @ParameterizedTest(name = "Upper case for {0}") @MethodSource("getNames") - fun parameterizedMethodTest (names: Pair) { + fun parameterizedMethodTest(names: Pair) { assertEquals(names.second, names.first.uppercase()) } + @ParameterizedTest(name = "New FieldSource from 5.11") + @FieldSource("somePrimeNumbers") + fun parameterizedFieldTest(number: Int) { + for (i in 2 until number) { + assertNotEquals(0, number % i) + } + } + @Nested @DisplayName("Nested Class With Distinct Name") internal inner class NestedTestClass {