Skip to content

Commit fd6c07f

Browse files
authored
Add support for inherited tests (#288)
* Add new classes for verification of inherited tests (to :core and :runner) * Make instrumentation detect inherited JU5 tests from superclass and interfaces * Gradle 7.5.1 * Changelog
1 parent c3428b4 commit fd6c07f

File tree

12 files changed

+143
-18
lines changed

12 files changed

+143
-18
lines changed

instrumentation/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Change Log
44
## Unreleased
55
- Update formatting of instrumentation test names to prevent breaking generation of log files in newer versions of AGP (#263)
66
- Add support for test sharding (#270)
7+
- Add support for inherited tests (#288)
78

89
## 1.3.0 (2021-09-17)
910

instrumentation/core/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ junitPlatform {
7272

7373
tasks.withType<KotlinCompile> {
7474
kotlinOptions.jvmTarget = javaVersion.toString()
75+
kotlinOptions.freeCompilerArgs = listOf("-Xjvm-default=all")
7576
}
7677

7778
tasks.withType<Test> {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package de.mannodermaus.junit5.inheritance;
2+
3+
import static org.junit.jupiter.api.Assertions.assertNotNull;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
abstract class JavaAbstractClass {
8+
@Test
9+
void javaTest() {
10+
assertNotNull(getJavaFileName());
11+
}
12+
13+
abstract String getJavaFileName();
14+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package de.mannodermaus.junit5.inheritance;
2+
3+
import androidx.annotation.Nullable;
4+
5+
public class JavaAbstractClassTest extends JavaAbstractClass {
6+
@Nullable
7+
@Override
8+
public String getJavaFileName() {
9+
return "hello world";
10+
}
11+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package de.mannodermaus.junit5.inheritance;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
interface JavaInterface {
6+
@Test
7+
default void javaTest() {
8+
assert(getJavaValue() > 0L);
9+
}
10+
11+
long getJavaValue();
12+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package de.mannodermaus.junit5.inheritance;
2+
3+
public class JavaInterfaceTest implements JavaInterface {
4+
@Override
5+
public long getJavaValue() {
6+
return 4815162342L;
7+
}
8+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package de.mannodermaus.junit5.inheritance;
2+
3+
public class JavaMixedInterfaceTest implements JavaInterface, KotlinInterface {
4+
@Override
5+
public long getJavaValue() {
6+
return 4815162342L;
7+
}
8+
9+
@Override
10+
public int getKotlinValue() {
11+
return 10101010;
12+
}
13+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package de.mannodermaus.junit5.inheritance
2+
3+
import org.junit.jupiter.api.Assertions.assertNotNull
4+
import org.junit.jupiter.api.Test
5+
6+
abstract class KotlinAbstractClass {
7+
@Test
8+
fun kotlinTest() {
9+
assertNotNull(getKotlinFileName())
10+
}
11+
12+
abstract fun getKotlinFileName(): String?
13+
}
14+
15+
interface KotlinInterface {
16+
@Test
17+
fun kotlinTest() {
18+
assert(kotlinValue > 0)
19+
}
20+
21+
val kotlinValue: Int
22+
}
23+
24+
class KotlinAbstractClassTest : KotlinAbstractClass() {
25+
override fun getKotlinFileName() = "hello world"
26+
}
27+
28+
class KotlinInterfaceTest : KotlinInterface {
29+
override val kotlinValue: Int = 1337
30+
}
31+
32+
class KotlinMixedInterfaceTest : KotlinInterface, JavaInterface {
33+
override val kotlinValue: Int = 1337
34+
override fun getJavaValue(): Long = 1234L
35+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists

instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/extensions/ClassExt.kt

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,35 @@ private val jupiterTestAnnotations = listOf(
99
"org.junit.jupiter.api.TestFactory",
1010
"org.junit.jupiter.api.RepeatedTest",
1111
"org.junit.jupiter.api.TestTemplate",
12-
"org.junit.jupiter.params.ParameterizedTest"
12+
"org.junit.jupiter.params.ParameterizedTest",
1313
)
1414

15-
internal fun Class<*>.jupiterTestMethods(): List<Method> {
16-
val allJupiterMethods = mutableListOf<Method>()
15+
internal fun Class<*>.jupiterTestMethods(): Set<Method> =
16+
jupiterTestMethods(includeInherited = true)
17+
18+
private fun Class<*>.jupiterTestMethods(includeInherited: Boolean): Set<Method> = buildSet {
1719
try {
1820
// Check each method in the Class for the presence
19-
// of the well-known list of JUnit Jupiter annotations
20-
allJupiterMethods += declaredMethods.filter { method ->
21-
val annotationClassNames =
22-
method.declaredAnnotations.map { it.annotationClass.qualifiedName }
23-
jupiterTestAnnotations.firstOrNull { annotation ->
24-
annotationClassNames.contains(annotation)
25-
} != null
26-
}
21+
// of the well-known list of JUnit Jupiter annotations.
22+
addAll(declaredMethods.filterAnnotatedByJUnitJupiter())
2723

2824
// Recursively check inner classes as well
2925
declaredClasses.forEach { inner ->
30-
allJupiterMethods += inner.jupiterTestMethods()
26+
addAll(inner.jupiterTestMethods(includeInherited = false))
27+
}
28+
29+
// Attach methods from inherited superclass or (for Java) implemented interfaces, too
30+
if (includeInherited) {
31+
addAll(superclass?.jupiterTestMethods(includeInherited = true).orEmpty())
32+
interfaces.forEach { i -> addAll(i.jupiterTestMethods(includeInherited = true)) }
3133
}
3234
} catch (t: Throwable) {
3335
Log.w(LOG_TAG, "${t.javaClass.name} in 'hasJupiterTestMethods()' for $name", t)
3436
}
35-
36-
return allJupiterMethods
3737
}
38+
39+
private fun Array<Method>.filterAnnotatedByJUnitJupiter(): List<Method> =
40+
filter { method ->
41+
val names = method.declaredAnnotations.map { it.annotationClass.qualifiedName }
42+
jupiterTestAnnotations.any { it in names }
43+
}

0 commit comments

Comments
 (0)