test: migrate unit tests from JUnit 4 to JUnit 6 (#6276)

JUnit 4 is in maintenance mode, and 4.13.2 (February 2021) is its last
release. Move the unit tests to JUnit Jupiter 6.1.3: the JUnit BOM,
junit-jupiter and the Platform launcher replace junit:junit, and the
unit test tasks run on the JUnit Platform. The test sources change only
mechanically: imports, lifecycle annotations, TemporaryFolder to
@TempDir, and the message argument moved last in three assertions.

Jupiter skips an invalid @Test method, such as one that returns a
value, with only a warning, where JUnit 4 failed the run. Treat
discovery warnings as failures to keep that behavior.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Miheichev Aleksandr SergeevichandClaude Opus 5.5 authored and GitHub committed 2026-10-01 15:20:41 +08:00
1 parent 85b1f694c9
commit 5029d75475
23 files changed
+107 -93

No files matched your search

+14 -1
View File
@@ -149,6 +149,17 @@ android {
}
}
testOptions {
unitTests.all {
it.useJUnitPlatform()
// Treat Jupiter discovery warnings as failures: JUnit 4 failed on an invalid @Test
// method, such as one that returns a value, while Jupiter only warns and skips it.
// The key is @API(status = EXPERIMENTAL) in JUnit 6; after a JUnit upgrade, check
// that a temporary `@Test fun probe() = 1` still fails the unit test task.
it.systemProperty("junit.platform.discovery.issue.severity.critical", "WARNING")
}
}
}
dependencies {
@@ -201,7 +212,9 @@ dependencies {
implementation(libs.reorderable)
// Testing Libraries
testImplementation(libs.junit)
testImplementation(platform(libs.junit.bom))
testImplementation(libs.junit.jupiter)
testRuntimeOnly(libs.junit.platform.launcher)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
testImplementation(libs.mockito.core)
@@ -1,8 +1,8 @@
package com.v2ray.ang
import com.v2ray.ang.util.HttpUtil
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class HttpUtilTest {
@@ -1,10 +1,10 @@
package com.v2ray.ang
import com.v2ray.ang.util.Utils
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
/**
* Example local unit test, which will execute on the development machine (host).
@@ -1,9 +1,9 @@
package com.v2ray.ang.extension
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
class ListExtTest {
@@ -4,14 +4,14 @@ import android.util.Base64
import android.util.Log
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.dto.entities.ProfileItem
import org.junit.After
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.Before
import org.junit.Test
import org.junit.jupiter.api.AfterEach
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.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.MockedStatic
import org.mockito.Mockito
import org.mockito.Mockito.mockStatic
@@ -67,7 +67,7 @@ class ShadowsocksFmtTest {
return "$SS_SCHEME${base64Encoded}#${remarks.replace(" ", "%20")}"
}
@Before
@BeforeEach
fun setUp() {
mockLog = mockStatic(Log::class.java, Mockito.RETURNS_DEFAULTS)
@@ -113,7 +113,7 @@ class ShadowsocksFmtTest {
}
}
@After
@AfterEach
fun tearDown() {
mockLog.close()
mockBase64.close()
@@ -271,7 +271,7 @@ class ShadowsocksFmtTest {
val uri = ShadowsocksFmt.toUri(config)
// Verify URI does not include scheme (toUri returns without ss:// prefix)
assertFalse("toUri should not include scheme prefix", uri.startsWith(SS_SCHEME))
assertFalse(uri.startsWith(SS_SCHEME), "toUri should not include scheme prefix")
assertTrue(uri.contains("@example.com:8388"))
assertTrue(uri.contains("#Test%20Server"))
@@ -317,8 +317,8 @@ class ShadowsocksFmtTest {
// Verify toUri returns without scheme, so we need to prepend it
assertFalse(
"toUri should return URI without scheme",
regeneratedUri.startsWith(SS_SCHEME)
regeneratedUri.startsWith(SS_SCHEME),
"toUri should return URI without scheme"
)
val reparsed = ShadowsocksFmt.parse("$SS_SCHEME$regeneratedUri")
@@ -362,7 +362,7 @@ class ShadowsocksFmtTest {
val result = ShadowsocksFmt.parseSip002(ssUrl)
assertNotNull("Failed for method: $method", result)
assertNotNull(result, "Failed for method: $method")
assertEquals(method, result?.method)
}
}
@@ -1,9 +1,9 @@
package com.v2ray.ang.handler
import com.v2ray.ang.AppConfig.DEFAULT_SUBSCRIPTION_ID
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
class OrphanProfileCleanerTest {
@@ -2,9 +2,9 @@ package com.v2ray.ang.handler
import com.v2ray.ang.dto.entities.ProfileItem
import com.v2ray.ang.enums.EConfigType
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
class ProfileReplacementTest {
@@ -4,10 +4,10 @@ import android.util.Log
import com.tencent.mmkv.MMKV
import com.v2ray.ang.dto.entities.SubscriptionItem
import com.v2ray.ang.util.JsonUtil
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.Mockito.mockStatic
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
@@ -20,7 +20,7 @@ class SubscriptionIndexTest {
private val mainValues = mutableMapOf<String, String>()
private val subValues = mutableMapOf<String, String>()
@Before
@BeforeEach
fun prepareStorage() {
for ((storage, values) in listOf(main to mainValues, subs to subValues)) {
reset(storage)
@@ -99,7 +99,7 @@ class SubscriptionIndexTest {
private val subs: MMKV = mock()
private val settings: MMKV = mock()
@BeforeClass
@BeforeAll
@JvmStatic
fun initializeHandles() {
mockStatic(MMKV::class.java).use {
@@ -1,7 +1,7 @@
package com.v2ray.ang.root
import org.junit.Assert.*
import org.junit.Test
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import java.util.concurrent.TimeUnit
class RootProcessRunnerTest {
@@ -1,7 +1,7 @@
package com.v2ray.ang.root
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class RootProxyManagerTest {
@@ -6,12 +6,12 @@ import android.os.Looper
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertSame
import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertSame
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.mockito.Mockito.mockConstruction
import org.mockito.Mockito.mockStatic
import org.mockito.kotlin.any
@@ -5,9 +5,9 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
@@ -6,8 +6,8 @@ import com.google.zxing.DecodeHintType
import com.google.zxing.MultiFormatReader
import com.google.zxing.common.HybridBinarizer
import com.google.zxing.qrcode.QRCodeWriter
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import java.nio.ByteBuffer
class ScannerActivityTest {
@@ -1,8 +1,8 @@
package com.v2ray.ang.ui.apppicker
import android.app.Application
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.mockito.kotlin.mock
class AppPickerViewModelTest {
@@ -1,8 +1,8 @@
package com.v2ray.ang.ui.logcat
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
class LogcatEntryTest {
@Test
@@ -1,7 +1,7 @@
package com.v2ray.ang.ui.main
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class MainImportMenuTest {
@@ -1,11 +1,11 @@
package com.v2ray.ang.ui.main
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertNotEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
class MainTestRequestsTest {
@Test
@@ -1,9 +1,9 @@
package com.v2ray.ang.ui.main
import androidx.compose.runtime.saveable.SaverScope
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
class ServerDeleteTargetTest {
@Test
@@ -1,8 +1,8 @@
package com.v2ray.ang.ui.server
import org.junit.Assert.assertEquals
import org.junit.Assert.assertSame
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertSame
import org.junit.jupiter.api.Test
class ProxyChainMembersTest {
@Test
@@ -7,10 +7,10 @@ import android.content.pm.PackageManager
import android.provider.Settings
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.mockito.Mockito.mockConstruction
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -4,9 +4,9 @@ import android.app.Application
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.entities.AssetUrlCache
import com.v2ray.ang.dto.entities.AssetUrlItem
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.mockito.Mockito.mock
class UserAssetViewModelTest {
@@ -1,12 +1,11 @@
package com.v2ray.ang.util
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.io.File
import java.util.zip.ZipEntry
import java.util.zip.ZipException
@@ -14,13 +13,13 @@ import java.util.zip.ZipOutputStream
class ZipUtilTest {
@get:Rule
val temporaryFolder = TemporaryFolder()
@TempDir
lateinit var tempDir: File
@Test
fun unzipToFolderExtractsNestedFile() {
val archive = createArchive("nested/config" to "value".toByteArray())
val destination = File(temporaryFolder.root, "destination")
val destination = File(tempDir, "destination")
assertTrue(ZipUtil.unzipToFolder(archive, destination.absolutePath))
assertEquals("value", File(destination, "nested/config").readText())
@@ -29,7 +28,7 @@ class ZipUtilTest {
@Test
fun extractArchiveRejectsPathTraversal() {
val archive = createArchive("../outside" to "overwrite".toByteArray())
val outside = File(temporaryFolder.root, "outside")
val outside = File(tempDir, "outside")
assertArchiveRejected(archive)
@@ -100,7 +99,7 @@ class ZipUtilTest {
archive: File,
limits: ZipUtil.ExtractionLimits = extractionLimits(),
) {
val destination = File(temporaryFolder.root, "destination")
val destination = File(tempDir, "destination")
assertThrows(ZipException::class.java) {
ZipUtil.extractArchive(archive, destination, limits)
}
@@ -108,7 +107,7 @@ class ZipUtilTest {
}
private fun createArchive(vararg entries: Pair<String, ByteArray>): File {
val archive = temporaryFolder.newFile()
val archive = File.createTempFile("archive", ".zip", tempDir)
ZipOutputStream(archive.outputStream()).use { output ->
entries.forEach { (name, contents) ->
output.putNextEntry(ZipEntry(name))
+4 -2
View File
@@ -4,7 +4,7 @@ desugarJdkLibs = "2.1.5"
gradleLicensePlugin = "0.9.91"
kotlin = "2.4.20"
coreKtx = "1.19.0"
junit = "4.13.2"
junit = "6.1.3"
junitVersion = "1.3.0"
espressoCore = "3.7.0"
activity = "1.13.0"
@@ -26,7 +26,9 @@ coil = "3.6.3"
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
desugar-jdk-libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugarJdkLibs" }
gradle-license-plugin = { module = "com.jaredsburrows:gradle-license-plugin", version.ref = "gradleLicensePlugin" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
junit-bom = { group = "org.junit", name = "junit-bom", version.ref = "junit" }
junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter" }
junit-platform-launcher = { group = "org.junit.platform", name = "junit-platform-launcher" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
mmkv-static = { module = "com.tencent:mmkv-static", version.ref = "mmkvStatic" }