Build.VERSION.SDK_INT在本地单元测试中的存根值

Evg*_*tin 9 android unit-testing mockito powermock

我想知道是否还有存根的价值Build.Version.SDK_INT?假设我在以下行中有以下几行ClassUnderTest:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    //do work
}else{
    //do another work
}
Run Code Online (Sandbox Code Playgroud)

我怎样才能涵盖所有代码?

我的意思是我想用不同的SDK_INT运行两个测试来输入两个块.

有可能在android 本地单元测试中使用Mockito/ PowerMockito

谢谢

tos*_*inl 27

使用反射更改值.

 static void setFinalStatic(Field field, Object newValue) throws Exception {
    field.setAccessible(true);

    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

    field.set(null, newValue);
 }
Run Code Online (Sandbox Code Playgroud)

然后

 setFinalStatic(Build.VERSION.class.getField("SDK_INT"), 123);
Run Code Online (Sandbox Code Playgroud)

经过测试.作品.

  • setFinalStatic(Build.VERSION.class.getField("SDK_INT"),"123"); 如果这不起作用,可能PowerMockito超过mockito并模拟静态字段. (3认同)

Seb*_*ian 6

作为反射的替代方法,您可以使用您自己的类来检查API,然后使用Mockito在快速JVM单元测试中测试API依赖的逻辑。

示例类

import android.os.Build

class SdkChecker {

    fun deviceIsOreoOrAbove(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O

}
Run Code Online (Sandbox Code Playgroud)

示例测试方法

fun createNotificationChannel(notificationManager: NotificationManager) {
    if (sdkChecker.deviceIsOreoOrAbove()) { // This sdkChecker will be mocked
        // If you target Android 8.0 (API level 26) you need a channel
        notificationManager.createNotificationChannel()
    }
}
Run Code Online (Sandbox Code Playgroud)

示例单元测试

import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.verify
import com.nhaarman.mockito_kotlin.verifyZeroInteractions
import com.nhaarman.mockito_kotlin.whenever

@Test
fun createNotificationChannelOnOreoOrAbove() {
    whenever(mockSdkChecker.deviceIsOreoOrAbove()).thenReturn(true)

    testedClass.createNotificationChannel(mockNotificationManager)

    verify(mockNotificationManager).createNotificationChannel()
}

@Test
fun createNotificationChannelBelowOreo() {
    whenever(mockSdkChecker.deviceIsOreoOrAbove()).thenReturn(false)

    testedClass.createNotificationChannel(mockNotificationManager)

    verifyZeroInteractions(mockNotificationManager)
}
Run Code Online (Sandbox Code Playgroud)


Эва*_*ist 5

java 17的解决方案,2023 年

简单步骤:

1.添加通用方法

private fun setStaticFieldViaReflection(field: Field, value: Any) {
    field.isAccessible = true
    getModifiersField().also {
        it.isAccessible = true
        it.set(field, field.modifiers and Modifier.FINAL.inv())
    }
    field.set(null, value)
}

private fun getModifiersField(): Field {
    return try {
        Field::class.java.getDeclaredField("modifiers")
    } catch (e: NoSuchFieldException) {
        try {
            val getDeclaredFields0: Method =
                Class::class.java.getDeclaredMethod(
                    "getDeclaredFields0",
                    Boolean::class.javaPrimitiveType
                )
            getDeclaredFields0.isAccessible = true
            val fields = getDeclaredFields0.invoke(Field::class.java, false) as Array<Field>
            for (field in fields) {
                if ("modifiers" == field.name) {
                    return field
                }
            }
        } catch (ex: ReflectiveOperationException) {
            e.addSuppressed(ex)
        }
        throw e
    }
}
Run Code Online (Sandbox Code Playgroud)

2.将这些标志添加到gradle(其他情况可以更改)

android {
    ...

    testOptions {
        unitTests.all {
            jvmArgs(
                    "--add-opens", "java.base/java.lang=ALL-UNNAMED",
                    "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED"
            )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

3.使用示例

  private val testSocManufacturer = "test-soc-manufacturer"
  setStaticFieldViaReflection(
        Build::class.java.getDeclaredField("SOC_MANUFACTURER"),
        testSocManufacturer)

  private val testSocModel = "test-soc-model"
  setStaticFieldViaReflection(
        Build::class.java.getDeclaredField("SOC_MODEL"),
        testSocModel)

  setStaticFieldViaReflection(
        Build.VERSION::class.java.getDeclaredField("SDK_INT"),
        30)
Run Code Online (Sandbox Code Playgroud)