Skip to content

Commit

Permalink
Bump to JUnit 5.11 M1, try out FieldSource and clean up some internal…
Browse files Browse the repository at this point in the history
… method names
  • Loading branch information
mannodermaus committed Apr 28, 2024
1 parent 38ef72c commit fdc3d16
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 26 deletions.
6 changes: 3 additions & 3 deletions build-logic/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,53 @@ 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<Method> =
jupiterTestMethods(includeInherited = true)
internal fun Class<*>.findJupiterTestMethods(): Set<Method> =
findJupiterTestMethods(includeInherited = true)

private fun Class<*>.jupiterTestMethods(includeInherited: Boolean): Set<Method> = buildSet {
private fun Class<*>.findJupiterTestMethods(includeInherited: Boolean): Set<Method> = buildSet {
try {
// Check each method in the Class for the presence
// of the well-known list of JUnit Jupiter annotations.
addAll(declaredMethods.filterAnnotatedByJUnitJupiter())

// 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<Method>.filterAnnotatedByJUnitJupiter(): List<Method> =
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)
Original file line number Diff line number Diff line change
@@ -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

/**
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class AndroidJUnit5BuilderTests {
HasTaggedTest::class.java,
HasInheritedTestsFromClass::class.java,
HasInheritedTestsFromInterface::class.java,
HasMultipleInheritancesAndOverrides::class.java,
)

@TestFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class HasTaggedTest {

abstract class AbstractTestClass {
@Test
fun abstractTest() {
open fun abstractTest() {
}
}

Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<ActivityOne>) {
val count = repetitionInfo.currentRepetition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -107,10 +111,18 @@ class ExampleKotlinTest {

@ParameterizedTest(name = "Upper case for {0}")
@MethodSource("getNames")
fun parameterizedMethodTest (names: Pair<String, String>) {
fun parameterizedMethodTest(names: Pair<String, String>) {
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 {
Expand Down

0 comments on commit fdc3d16

Please sign in to comment.