From f1a8a66f2e66b33d7fa80aaca327bb9a21194a9f Mon Sep 17 00:00:00 2001 From: one-kash <26795040+one-kash@users.noreply.github.com> Date: Wed, 21 Jan 2026 19:44:30 +0000 Subject: [PATCH] Migrate Android instrumented tests from JUnit 5 to JUnit 4 JUnit 5 tests don't run on Android API 21-25 due to limitations of the android-junit5 plugin. This change migrates the android-test module to use JUnit 4 for broader device compatibility. Changes: - Replace JUnit 5 annotations with JUnit 4 equivalents - @BeforeEach -> @Before - @AfterEach -> @After - @Disabled -> @Ignore - @RegisterExtension -> removed (manual lifecycle management) - @Tag -> removed (use @Category if needed) - Replace JUnit 5 assertions/assumptions with JUnit 4 - Replace TestAbortedException with AssumptionViolatedException - Add @RunWith(AndroidJUnit4::class) to test classes - Remove de.mannodermaus.android-junit5 plugin - Update test instrumentation runner configuration - Manually start/stop MockWebServer in test setup/teardown - Simplify client creation without OkHttpClientTestRule Fixes #9134 --- android-test/build.gradle.kts | 18 +--- .../java/okhttp/android/test/OkHttpTest.kt | 87 ++++++++----------- .../okhttp/android/test/SingleAndroidTest.kt | 2 +- .../okhttp/android/test/StrictModeTest.kt | 11 +-- .../android/test/alpn/AlpnOverrideTest.kt | 7 +- .../test/letsencrypt/LetsEncryptClientTest.kt | 7 +- .../android/test/sni/SniOverrideTest.kt | 9 +- 7 files changed, 62 insertions(+), 79 deletions(-) diff --git a/android-test/build.gradle.kts b/android-test/build.gradle.kts index e704ee806d08..f3bc46135259 100644 --- a/android-test/build.gradle.kts +++ b/android-test/build.gradle.kts @@ -3,7 +3,6 @@ plugins { id("com.android.library") kotlin("android") - id("de.mannodermaus.android-junit5") } val androidBuild = property("androidBuild").toString().toBoolean() @@ -16,12 +15,10 @@ android { defaultConfig { minSdk = 21 - // Make sure to use the AndroidJUnitRunner (or a sub-class) in order to hook in the JUnit 5 Test Builder + // Use standard AndroidJUnitRunner for JUnit 4 tests (compatible with API 21+) testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunnerArguments += mapOf( - "runnerBuilder" to "de.mannodermaus.junit5.AndroidJUnit5Builder", - "notPackage" to "org.bouncycastle", - "configurationParameters" to "junit.jupiter.extensions.autodetection.enabled=true" + "notPackage" to "org.bouncycastle" ) } @@ -107,13 +104,6 @@ dependencies { androidTestImplementation(libs.squareup.okio.fakefilesystem) androidTestImplementation(libs.androidx.test.runner) - androidTestImplementation(libs.junit.jupiter.api) - androidTestImplementation(libs.junit5android.core) - androidTestRuntimeOnly(libs.junit5android.runner) -} - -junitPlatform { - filters { - excludeTags("Remote") - } + // Use JUnit 4 for Android instrumented tests (compatible with API 21+) + androidTestImplementation(libs.junit) } diff --git a/android-test/src/androidTest/java/okhttp/android/test/OkHttpTest.kt b/android-test/src/androidTest/java/okhttp/android/test/OkHttpTest.kt index b960cf3be026..ec0326942d7d 100644 --- a/android-test/src/androidTest/java/okhttp/android/test/OkHttpTest.kt +++ b/android-test/src/androidTest/java/okhttp/android/test/OkHttpTest.kt @@ -44,7 +44,6 @@ import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager import mockwebserver3.MockResponse import mockwebserver3.MockWebServer -import mockwebserver3.junit5.StartStop import okhttp3.Cache import okhttp3.Call import okhttp3.CallEvent.CallEnd @@ -77,7 +76,6 @@ import okhttp3.Gzip import okhttp3.Headers import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient -import okhttp3.OkHttpClientTestRule import okhttp3.Protocol import okhttp3.Request import okhttp3.TlsVersion @@ -90,7 +88,6 @@ import okhttp3.internal.platform.AndroidPlatform import okhttp3.internal.platform.Platform import okhttp3.internal.platform.PlatformRegistry import okhttp3.logging.LoggingEventListener -import okhttp3.testing.PlatformRule import okhttp3.tls.HandshakeCertificates import okhttp3.tls.internal.TlsUtil.localhost import okhttp3.zstd.Zstd @@ -98,38 +95,25 @@ import okio.ByteString.Companion.toByteString import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider import org.conscrypt.Conscrypt -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertNotNull -import org.junit.jupiter.api.Assertions.assertNull -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Assertions.fail -import org.junit.jupiter.api.Assumptions.assumeTrue -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Tag -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.RegisterExtension -import org.opentest4j.TestAbortedException - -@Tag("Slow") +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.After +import org.junit.Assume.assumeTrue +import org.junit.Before +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.AssumptionViolatedException + +@RunWith(AndroidJUnit4::class) class OkHttpTest { - @Suppress("RedundantVisibilityModifier") - @JvmField - @RegisterExtension - public val platform = PlatformRule() - - @Suppress("RedundantVisibilityModifier") - @JvmField - @RegisterExtension - public val clientTestRule = - OkHttpClientTestRule().apply { - logger = Logger.getLogger(OkHttpTest::class.java.name) - } - private var client: OkHttpClient = - clientTestRule - .newClientBuilder() + OkHttpClient.Builder() .addInterceptor(CompressionInterceptor(Zstd, Brotli, Gzip)) .build() @@ -141,13 +125,18 @@ class OkHttpTest { private val handshakeCertificates = localhost() - @StartStop private val server = MockWebServer() - @BeforeEach + @Before fun setup() { // Needed because of Platform.resetForTests PlatformRegistry.applicationContext = ApplicationProvider.getApplicationContext() + server.start() + } + + @After + fun tearDown() { + server.shutdown() } @Test @@ -225,7 +214,7 @@ class OkHttpTest { OkHttpClient .Builder() .eventListenerFactory( - clientTestRule.wrap( + ( object : EventListener() { override fun connectionAcquired( call: Call, @@ -296,7 +285,7 @@ class OkHttpTest { try { ProviderInstaller.installIfNeeded(InstrumentationRegistry.getInstrumentation().targetContext) } catch (gpsnae: GooglePlayServicesNotAvailableException) { - throw TestAbortedException("Google Play Services not available", gpsnae) + throw AssumptionViolatedException("Google Play Services not available", gpsnae) } val request = Request.Builder().url("https://facebook.com/robots.txt").build() @@ -308,7 +297,7 @@ class OkHttpTest { OkHttpClient .Builder() .eventListenerFactory( - clientTestRule.wrap( + ( object : EventListener() { override fun connectionAcquired( call: Call, @@ -340,7 +329,7 @@ class OkHttpTest { try { ProviderInstaller.installIfNeeded(InstrumentationRegistry.getInstrumentation().targetContext) } catch (gpsnae: GooglePlayServicesNotAvailableException) { - throw TestAbortedException("Google Play Services not available", gpsnae) + throw AssumptionViolatedException("Google Play Services not available", gpsnae) } val clientCertificates = @@ -389,7 +378,7 @@ class OkHttpTest { client .newBuilder() .eventListenerFactory( - clientTestRule.wrap( + ( object : EventListener() { override fun connectionAcquired( call: Call, @@ -452,7 +441,7 @@ class OkHttpTest { } @Test - @Disabled("cleartext required for additional okhttp wide tests") + @Ignore("cleartext required for additional okhttp wide tests") fun testHttpRequestBlocked() { assumeTrue(Build.VERSION.SDK_INT >= 23) @@ -479,7 +468,7 @@ class OkHttpTest { ) @Test - @Disabled + @Ignore fun testSSLFeatures() { assumeNetwork() @@ -581,7 +570,7 @@ class OkHttpTest { client = client .newBuilder() - .eventListenerFactory(clientTestRule.wrap(eventRecorder)) + .eventListenerFactory((eventRecorder)) .build() server.enqueue(MockResponse(body = "abc1")) @@ -652,7 +641,7 @@ class OkHttpTest { client .newBuilder() .eventListenerFactory( - clientTestRule.wrap( + ( object : EventListener() { override fun connectionAcquired( call: Call, @@ -697,7 +686,7 @@ class OkHttpTest { client = client .newBuilder() - .eventListenerFactory(clientTestRule.wrap(LoggingEventListener.Factory())) + .eventListenerFactory((LoggingEventListener.Factory())) .build() val dohDns = buildCloudflareIp(client) @@ -872,7 +861,7 @@ class OkHttpTest { } @Test - @Disabled("breaks conscrypt test") + @Ignore("breaks conscrypt test") fun testBouncyCastleRequest() { assumeNetwork() @@ -901,7 +890,7 @@ class OkHttpTest { .newBuilder() .sslSocketFactory(sslContext.socketFactory, trustManager) .eventListenerFactory( - clientTestRule.wrap( + ( object : EventListener() { override fun connectionAcquired( call: Call, @@ -930,7 +919,7 @@ class OkHttpTest { } @Test - @Disabled("TODO: currently logging okhttp3.internal.concurrent.TaskRunner, okhttp3.internal.http2.Http2") + @Ignore("TODO: currently logging okhttp3.internal.concurrent.TaskRunner, okhttp3.internal.http2.Http2") fun testLoggingLevels() { enableTls() @@ -1076,7 +1065,7 @@ class OkHttpTest { try { InetAddress.getByName("www.google.com") } catch (uhe: UnknownHostException) { - throw TestAbortedException(uhe.message, uhe) + throw AssumptionViolatedException(uhe.message, uhe) } } diff --git a/android-test/src/androidTest/java/okhttp/android/test/SingleAndroidTest.kt b/android-test/src/androidTest/java/okhttp/android/test/SingleAndroidTest.kt index 3844948b197f..2697f5cbfaae 100644 --- a/android-test/src/androidTest/java/okhttp/android/test/SingleAndroidTest.kt +++ b/android-test/src/androidTest/java/okhttp/android/test/SingleAndroidTest.kt @@ -23,7 +23,7 @@ import okhttp3.ConnectionPool import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.tls.internal.TlsUtil.localhost -import org.junit.jupiter.api.Test +import org.junit.Test /** * This single Junit 4 test is our Android test suite on API 21-25. diff --git a/android-test/src/androidTest/java/okhttp/android/test/StrictModeTest.kt b/android-test/src/androidTest/java/okhttp/android/test/StrictModeTest.kt index b75225373bcf..91b479526dba 100644 --- a/android-test/src/androidTest/java/okhttp/android/test/StrictModeTest.kt +++ b/android-test/src/androidTest/java/okhttp/android/test/StrictModeTest.kt @@ -27,16 +27,17 @@ import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.internal.platform.Platform -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.parallel.Isolated +import org.junit.After +import org.junit.Test +import org.junit.runner.RunWith +import androidx.test.ext.junit.runners.AndroidJUnit4 -@Isolated +@RunWith(AndroidJUnit4::class) @SdkSuppress(minSdkVersion = 28) class StrictModeTest { private val violations = mutableListOf() - @AfterEach + @After fun cleanup() { StrictMode.setThreadPolicy( ThreadPolicy diff --git a/android-test/src/androidTest/java/okhttp/android/test/alpn/AlpnOverrideTest.kt b/android-test/src/androidTest/java/okhttp/android/test/alpn/AlpnOverrideTest.kt index 05e575f904e3..a30010d3543d 100644 --- a/android-test/src/androidTest/java/okhttp/android/test/alpn/AlpnOverrideTest.kt +++ b/android-test/src/androidTest/java/okhttp/android/test/alpn/AlpnOverrideTest.kt @@ -28,13 +28,14 @@ import okhttp3.DelegatingSSLSocketFactory import okhttp3.EventListener import okhttp3.OkHttpClient import okhttp3.Request -import org.junit.jupiter.api.Tag -import org.junit.jupiter.api.Test +import org.junit.Test +import org.junit.runner.RunWith +import androidx.test.ext.junit.runners.AndroidJUnit4 /** * Tests for ALPN overriding on Android. */ -@Tag("Remote") +@RunWith(AndroidJUnit4::class) class AlpnOverrideTest { class CustomSSLSocketFactory( delegate: SSLSocketFactory, diff --git a/android-test/src/androidTest/java/okhttp/android/test/letsencrypt/LetsEncryptClientTest.kt b/android-test/src/androidTest/java/okhttp/android/test/letsencrypt/LetsEncryptClientTest.kt index bf73d49fcbba..0c1c45493126 100644 --- a/android-test/src/androidTest/java/okhttp/android/test/letsencrypt/LetsEncryptClientTest.kt +++ b/android-test/src/androidTest/java/okhttp/android/test/letsencrypt/LetsEncryptClientTest.kt @@ -24,13 +24,14 @@ import okhttp3.Protocol import okhttp3.Request import okhttp3.tls.HandshakeCertificates import okhttp3.tls.decodeCertificatePem -import org.junit.jupiter.api.Tag -import org.junit.jupiter.api.Test +import org.junit.Test +import org.junit.runner.RunWith +import androidx.test.ext.junit.runners.AndroidJUnit4 /** * Test for new Let's Encrypt Root Certificate. */ -@Tag("Remote") +@RunWith(AndroidJUnit4::class) class LetsEncryptClientTest { @Test fun get() { // These tests wont actually run before Android 8.0 as per diff --git a/android-test/src/androidTest/java/okhttp/android/test/sni/SniOverrideTest.kt b/android-test/src/androidTest/java/okhttp/android/test/sni/SniOverrideTest.kt index cdcce2c0dfbe..7cb41c9de834 100644 --- a/android-test/src/androidTest/java/okhttp/android/test/sni/SniOverrideTest.kt +++ b/android-test/src/androidTest/java/okhttp/android/test/sni/SniOverrideTest.kt @@ -30,14 +30,15 @@ import okhttp3.Dns import okhttp3.OkHttpClient import okhttp3.Protocol import okhttp3.Request -import org.junit.jupiter.api.Assumptions.assumeTrue -import org.junit.jupiter.api.Tag -import org.junit.jupiter.api.Test +import org.junit.Assume.assumeTrue +import org.junit.Test +import org.junit.runner.RunWith +import androidx.test.ext.junit.runners.AndroidJUnit4 /** * Tests for SNI overriding on Android. */ -@Tag("Remote") +@RunWith(AndroidJUnit4::class) class SniOverrideTest { var client = OkHttpClient