Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for inherited tests #288

Merged
merged 4 commits into from
Oct 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions instrumentation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Change Log
## Unreleased
- Update formatting of instrumentation test names to prevent breaking generation of log files in newer versions of AGP (#263)
- Add support for test sharding (#270)
- Add support for inherited tests (#288)

## 1.3.0 (2021-09-17)

Expand Down
1 change: 1 addition & 0 deletions instrumentation/core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ junitPlatform {

tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = javaVersion.toString()
kotlinOptions.freeCompilerArgs = listOf("-Xjvm-default=all")
}

tasks.withType<Test> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package de.mannodermaus.junit5.inheritance;

import static org.junit.jupiter.api.Assertions.assertNotNull;

import org.junit.jupiter.api.Test;

abstract class JavaAbstractClass {
@Test
void javaTest() {
assertNotNull(getJavaFileName());
}

abstract String getJavaFileName();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package de.mannodermaus.junit5.inheritance;

import androidx.annotation.Nullable;

public class JavaAbstractClassTest extends JavaAbstractClass {
@Nullable
@Override
public String getJavaFileName() {
return "hello world";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.mannodermaus.junit5.inheritance;

import org.junit.jupiter.api.Test;

interface JavaInterface {
@Test
default void javaTest() {
assert(getJavaValue() > 0L);
}

long getJavaValue();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package de.mannodermaus.junit5.inheritance;

public class JavaInterfaceTest implements JavaInterface {
@Override
public long getJavaValue() {
return 4815162342L;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package de.mannodermaus.junit5.inheritance;

public class JavaMixedInterfaceTest implements JavaInterface, KotlinInterface {
@Override
public long getJavaValue() {
return 4815162342L;
}

@Override
public int getKotlinValue() {
return 10101010;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package de.mannodermaus.junit5.inheritance

import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test

abstract class KotlinAbstractClass {
@Test
fun kotlinTest() {
assertNotNull(getKotlinFileName())
}

abstract fun getKotlinFileName(): String?
}

interface KotlinInterface {
@Test
fun kotlinTest() {
assert(kotlinValue > 0)
}

val kotlinValue: Int
}

class KotlinAbstractClassTest : KotlinAbstractClass() {
override fun getKotlinFileName() = "hello world"
}

class KotlinInterfaceTest : KotlinInterface {
override val kotlinValue: Int = 1337
}

class KotlinMixedInterfaceTest : KotlinInterface, JavaInterface {
override val kotlinValue: Int = 1337
override fun getJavaValue(): Long = 1234L
}
2 changes: 1 addition & 1 deletion instrumentation/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,35 @@ private val jupiterTestAnnotations = listOf(
"org.junit.jupiter.api.TestFactory",
"org.junit.jupiter.api.RepeatedTest",
"org.junit.jupiter.api.TestTemplate",
"org.junit.jupiter.params.ParameterizedTest"
"org.junit.jupiter.params.ParameterizedTest",
)

internal fun Class<*>.jupiterTestMethods(): List<Method> {
val allJupiterMethods = mutableListOf<Method>()
internal fun Class<*>.jupiterTestMethods(): Set<Method> =
jupiterTestMethods(includeInherited = true)

private fun Class<*>.jupiterTestMethods(includeInherited: Boolean): Set<Method> = buildSet {
try {
// Check each method in the Class for the presence
// of the well-known list of JUnit Jupiter annotations
allJupiterMethods += declaredMethods.filter { method ->
val annotationClassNames =
method.declaredAnnotations.map { it.annotationClass.qualifiedName }
jupiterTestAnnotations.firstOrNull { annotation ->
annotationClassNames.contains(annotation)
} != null
}
// of the well-known list of JUnit Jupiter annotations.
addAll(declaredMethods.filterAnnotatedByJUnitJupiter())

// Recursively check inner classes as well
declaredClasses.forEach { inner ->
allJupiterMethods += inner.jupiterTestMethods()
addAll(inner.jupiterTestMethods(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)) }
}
} catch (t: Throwable) {
Log.w(LOG_TAG, "${t.javaClass.name} in 'hasJupiterTestMethods()' for $name", t)
}

return allJupiterMethods
}

private fun Array<Method>.filterAnnotatedByJUnitJupiter(): List<Method> =
filter { method ->
val names = method.declaredAnnotations.map { it.annotationClass.qualifiedName }
jupiterTestAnnotations.any { it in names }
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,24 @@ class HasTaggedTest {
fun method() {

}
}
}

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

interface AbstractTestInterface {
@Test
fun interfaceTest() {
}
}

class HasInheritedTestsFromClass : AbstractTestClass() {
@Test
fun method() {
}
}

class HasInheritedTestsFromInterface : AbstractTestInterface
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package de.mannodermaus.junit5.internal
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import de.mannodermaus.junit5.DoesntHaveTestMethods
import de.mannodermaus.junit5.HasInheritedTestsFromClass
import de.mannodermaus.junit5.HasInheritedTestsFromInterface
import de.mannodermaus.junit5.HasInnerClassWithTest
import de.mannodermaus.junit5.HasParameterizedTest
import de.mannodermaus.junit5.HasRepeatedTest
Expand Down Expand Up @@ -49,7 +51,7 @@ class ExtensionsTests {
)
AndroidJUnit5(klass, params).run(notifier)

assertWithMessage("Executed ${listener.count()} instead of $expectExecutedTests tests: '${listener.methodNames()}'")
assertWithMessage("Executed ${listener.count()} instead of $expectExecutedTests tests on class '${klass.simpleName}': '${listener.methodNames()}'")
.that(listener.count())
.isEqualTo(expectExecutedTests)
}
Expand Down Expand Up @@ -87,7 +89,9 @@ class ExtensionsTests {
Arguments.of(HasTestFactory::class.java, 2),
Arguments.of(HasTestTemplate::class.java, 2),
Arguments.of(HasParameterizedTest::class.java, 2),
Arguments.of(HasInnerClassWithTest::class.java, 1)
Arguments.of(HasInnerClassWithTest::class.java, 1),
Arguments.of(HasInheritedTestsFromClass::class.java, 2),
Arguments.of(HasInheritedTestsFromInterface::class.java, 1),
)
}
}
Expand Down