Skip to content

Commit 57de623

Browse files
committed
Update instrumentation deps + gracefully ignore unloadable classes during test discovery
1 parent ee44315 commit 57de623

File tree

11 files changed

+95
-49
lines changed

11 files changed

+95
-49
lines changed

build-logic/src/main/kotlin/Dependencies.kt

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,25 @@ object libs {
88
const val junitPlatform = "1.10.2"
99

1010
const val composeBom = "2024.04.00"
11-
const val androidXTest = "1.5.0"
11+
const val androidXTest = "1.6.1"
12+
const val androidXTestAnnotation = "1.0.1"
13+
const val androidXTestMonitor = "1.7.1"
1214
const val composeCompiler = "1.5.11"
1315

14-
const val activityCompose = "1.8.2"
16+
const val activityCompose = "1.9.0"
1517
const val apiGuardian = "1.1.2"
16-
const val coroutines = "1.8.0"
18+
const val coroutines = "1.8.1"
1719
const val dokka = "1.9.20"
18-
const val espresso = "3.5.1"
20+
const val espresso = "3.6.1"
1921
const val javaSemver = "0.10.2"
2022
const val junit4 = "4.13.2"
2123
const val konfToml = "1.1.2"
2224
const val korte = "2.4.12"
23-
const val mockitoCore = "5.11.0"
24-
const val mockitoKotlin = "5.2.1"
25-
const val robolectric = "4.12"
25+
const val mockitoCore = "5.12.0"
26+
const val mockitoKotlin = "5.4.0"
27+
const val robolectric = "4.13"
2628
const val shadow = "8.1.1"
27-
const val truth = "1.4.2"
29+
const val truth = "1.4.4"
2830
}
2931

3032
object plugins {
@@ -72,9 +74,10 @@ object libs {
7274
const val truthJava8Extensions = "com.google.truth.extensions:truth-java8-extension:${versions.truth}"
7375
const val robolectric = "org.robolectric:robolectric:${versions.robolectric}"
7476

77+
const val androidXTestAnnotation = "androidx.test:annotation:${versions.androidXTestAnnotation}"
7578
const val androidXTestCore = "androidx.test:core:${versions.androidXTest}"
7679
const val androidXTestRunner = "androidx.test:runner:${versions.androidXTest}"
77-
const val androidXTestMonitor = "androidx.test:monitor:${versions.androidXTest}"
80+
const val androidXTestMonitor = "androidx.test:monitor:${versions.androidXTestMonitor}"
7881
const val espressoCore = "androidx.test.espresso:espresso-core:${versions.espresso}"
7982

8083
const val composeUiTest = "androidx.compose.ui:ui-test"

build-logic/src/main/kotlin/Environment.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ object Artifacts {
102102
const val latestStableVersion = "1.4.0"
103103

104104
val Core = Deployed(
105-
platform = Android(minSdk = 14),
105+
platform = Android(minSdk = 19),
106106
groupId = groupId,
107107
artifactId = "android-test-core",
108108
currentVersion = currentVersion,
@@ -112,7 +112,7 @@ object Artifacts {
112112
)
113113

114114
val Extensions = Deployed(
115-
platform = Android(minSdk = 14),
115+
platform = Android(minSdk = 19),
116116
groupId = groupId,
117117
artifactId = "android-test-extensions",
118118
currentVersion = currentVersion,
@@ -122,7 +122,7 @@ object Artifacts {
122122
)
123123

124124
val Runner = Deployed(
125-
platform = Android(minSdk = 14),
125+
platform = Android(minSdk = 19),
126126
groupId = groupId,
127127
artifactId = "android-test-runner",
128128
currentVersion = currentVersion,

instrumentation/.idea/runConfigurations/Compose__Run_Instrumentation_Tests.xml

Lines changed: 13 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

instrumentation/.idea/runConfigurations/Core__Run_Instrumentation_Tests.xml

Lines changed: 13 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

instrumentation/.idea/runConfigurations/Sample__Run_Instrumentation_Tests.xml

Lines changed: 13 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

instrumentation/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Change Log
99
- Prevent test methods incorrectly defined as Kotlin top-level functions from messing up Android's internal test counting, causing issues like "Expected N+1 tests, received N" (#316)
1010
- Prevent test classes ignored by a tag from being considered for test execution, causing issues like "Expected N+1 tests, received N" (#298)
1111
- Improve integration with Android Test Orchestrator and remove the need for `@UseTechnicalNames` (#337)
12+
- Raise minimum supported API level of `core`, `extensions` and `runner` modules from 14 to 19
13+
- Gracefully ignore unloadable classes during test discovery, e.g. those that access JVM-only APIs like `sun.*`
1214

1315
## 1.4.0 (2023-11-05)
1416

instrumentation/extensions/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ configurations.all {
8585
}
8686

8787
dependencies {
88+
implementation(libs.androidXTestAnnotation)
8889
implementation(libs.androidXTestRunner)
8990
implementation(libs.junitJupiterApi)
9091

instrumentation/extensions/src/main/kotlin/de/mannodermaus/junit5/extensions/GrantPermissionExtension.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package de.mannodermaus.junit5.extensions
33
import android.Manifest
44
import android.annotation.SuppressLint
55
import android.os.Build
6-
import androidx.annotation.VisibleForTesting
76
import androidx.test.annotation.ExperimentalTestApi
87
import androidx.test.internal.platform.ServiceLoaderWrapper.loadSingleService
98
import androidx.test.internal.platform.content.PermissionGranter

instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/AndroidJUnit5Builder.kt

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,8 @@ public class AndroidJUnit5Builder : RunnerBuilder() {
4242
// AND that integration with applications NOT using JUnit 5 for UI tests still works.
4343
//
4444
// First, verify the existence of junit-jupiter-api on the classpath.
45-
// Then, verify that the JUnitPlatform Runner is available on the classpath.
46-
// It is VERY important to perform this check BEFORE verifying the existence
47-
// of the AndroidJUnit5 Runner, which inherits from JUnitPlatform. If this is omitted,
48-
// an uncatchable verification error will be raised, rendering instrumentation testing
49-
// for applications without the desire to include JUnit 5 effectively useless.
50-
// The simple Class.forName() check however will catch this allowed inconsistency,
51-
// and gracefully abort.
45+
// Then, verify that the Android JUnit 5 Runner is available.
5246
Class.forName("org.junit.jupiter.api.Test")
53-
Class.forName("org.junit.platform.runner.JUnitPlatform")
5447
Class.forName("de.mannodermaus.junit5.internal.runners.AndroidJUnit5")
5548
true
5649
} catch (e: Throwable) {
@@ -69,7 +62,7 @@ public class AndroidJUnit5Builder : RunnerBuilder() {
6962

7063
@Throws(Throwable::class)
7164
override fun runnerForClass(testClass: Class<*>): Runner? {
72-
// Ignore a bunch of class in internal packages
65+
// Ignore a bunch of classes in internal packages
7366
if (testClass.isInIgnorablePackage) return null
7467

7568
try {
@@ -99,6 +92,7 @@ public class AndroidJUnit5Builder : RunnerBuilder() {
9992
"androidx.",
10093
"com.android.",
10194
"kotlin.",
95+
"kotlinx.",
10296
)
10397

10498
private val Class<*>.isInIgnorablePackage: Boolean get() {

instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnit5.kt

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package de.mannodermaus.junit5.internal.runners
22

3-
import android.annotation.SuppressLint
3+
import androidx.annotation.RequiresApi
44
import androidx.annotation.VisibleForTesting
55
import de.mannodermaus.junit5.internal.runners.notification.ParallelRunNotifier
6+
import org.junit.platform.commons.JUnitException
7+
import org.junit.platform.engine.ConfigurationParameters
68
import org.junit.platform.engine.discovery.MethodSelector
9+
import org.junit.platform.launcher.TestPlan
710
import org.junit.platform.launcher.core.LauncherFactory
811
import org.junit.runner.Runner
912
import org.junit.runner.notification.RunNotifier
13+
import java.util.Optional
1014

1115
/**
1216
* JUnit Runner implementation using the JUnit Platform as its backbone.
@@ -15,18 +19,29 @@ import org.junit.runner.notification.RunNotifier
1519
*
1620
* @see org.junit.platform.runner.JUnitPlatform
1721
*/
18-
@SuppressLint("NewApi")
22+
@RequiresApi(26)
1923
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
2024
internal class AndroidJUnit5(
2125
private val testClass: Class<*>,
2226
paramsSupplier: () -> AndroidJUnit5RunnerParams = AndroidJUnit5RunnerParams.Companion::create,
2327
) : Runner() {
28+
private companion object {
29+
private val emptyConfigurationParameters = object : ConfigurationParameters {
30+
override fun get(key: String?) = Optional.empty<String>()
31+
override fun getBoolean(key: String?) = Optional.empty<Boolean>()
32+
override fun keySet() = emptySet<String>()
33+
34+
@Deprecated("Deprecated in Java", ReplaceWith("keySet().size"))
35+
override fun size() = 0
36+
}
37+
38+
private val emptyTestPlan = TestPlan.from(emptyList(), emptyConfigurationParameters)
39+
}
2440

2541
private val launcher = LauncherFactory.create()
2642
private val testTree by lazy { generateTestTree(paramsSupplier()) }
2743

28-
override fun getDescription() =
29-
testTree.suiteDescription
44+
override fun getDescription() = testTree.suiteDescription
3045

3146
override fun run(notifier: RunNotifier) {
3247
// Finally, launch the test plan on the JUnit Platform
@@ -56,8 +71,20 @@ internal class AndroidJUnit5(
5671
)
5772
}
5873

74+
val testPlan = try {
75+
launcher.discover(request)
76+
} catch (e: JUnitException) {
77+
// Each class in scope is given to the runner,
78+
// but some may fail to be loaded by the class loader
79+
// (e.g. when they are tailored to JVM work and reference sun.* classes
80+
// or anything else not present in the Android runtime).
81+
// Log those to console, but discard them from being considered at all
82+
e.printStackTrace()
83+
emptyTestPlan
84+
}
85+
5986
return AndroidJUnitPlatformTestTree(
60-
testPlan = launcher.discover(request),
87+
testPlan = testPlan,
6188
testClass = testClass,
6289
needLegacyFormat = isIsolatedMethodRun || isUsingOrchestrator,
6390
isParallelExecutionEnabled = params.isParallelExecutionEnabled,

0 commit comments

Comments
 (0)