android - 使用 Powermock2 和 Kotlin 模拟静态类

标签 android kotlin powermock powermockito

在 Android 中,我一直在使用 Powermock 的 1.6.1 版本,所有这些实现都非常适用于静态。 当我更改为 2.0.0-beta.5 时,它现在根本不起作用。事实上,从我之前的 1.6.1 升级到 1.7.1 甚至都无法正常工作。

我有这个实现:

// Power Mockito
testImplementation "org.powermock:powermock-api-mockito2:2.0.0-beta.5"
testImplementation "org.powermock:powermock-module-junit4-rule-agent:2.0.0-beta.5"
testImplementation "org.powermock:powermock-module-junit4:2.0.0-beta.5"
//testImplementation 'org.powermock:powermock-module-junit4-rule:2.0.0-beta.5'

// Mockito
testImplementation "org.mockito:mockito-core:2.11.0"
testImplementation "com.nhaarman:mockito-kotlin-kt1.1:1.5.0"
androidTestImplementation("com.nhaarman:mockito-kotlin-kt1.1:1.5.0", {
    exclude group: 'org.mockito', module: 'mockito-core'
})
androidTestImplementation 'org.mockito:mockito-android:2.11.0'

我正在尝试以与 1.6.1 相同的方式模拟静态:

@RunWith(PowerMockRunner::class)
@PrepareForTest(SharedPreferencesHelper.Companion::class, ConfigUseCaseTests::class)
class ConfigUseCaseTests {

    lateinit var context: Context

    @Before
    fun setUp() {
        context = mock()
    }

    @Test
    fun getConfigs_fromJson() {
        PowerMockito.mockStatic(SharedPreferencesHelper.Companion::class.java)

        val instance = mock<SharedPreferencesHelper.Companion>()
        doReturn("foo")
                .whenever(instance)
                .loadString(isA(), anyString(), anyString(), anyString())
//        whenever(instance.loadString(isA(), anyString(), anyString(), anyString())).thenReturn("foo") // This shows the same error
        PowerMockito.whenNew(SharedPreferencesHelper.Companion::class.java)
                .withAnyArguments()
                .thenReturn(instance)

        val mockedFoo = instance.loadString(context, "", "", "") // This shows "foo"

        val mockedCompanion = SharedPreferencesHelper.loadString(context, "", "", "") // This is throwing NullPointerException

        Assert.assertEquals(mockedCompanion, "foo")
    }
}

我的 SharedPreferencesHelper 看起来像:

class SharedPreferencesHelper {
    companion object {

        @Suppress("NON_FINAL_MEMBER_IN_OBJECT")
        open fun loadString(context: Context, fileName: String, key: String, defaultValue: String?): String? {
            val sharedPreferences = getWithFileName(context, fileName)
            return if (!sharedPreferences.contains(key)) {
                defaultValue
            } else {
                sharedPreferences.getString(key, defaultValue)
            }
        }
    }
}

我尝试过使用 open,但没有成功。

异常(exception):(我完全不明白)

java.lang.NullPointerException
    at my.package.ConfigUseCaseTests.getConfigs_fromJson(ConfigUseCaseTests.kt:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:326)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:310)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:131)

我可以说,有时它有效!我正在添加视频,因为它有时会发生看起来很神奇: https://youtu.be/YZObVLcERBo (看中间和结尾)

最佳答案

编译时创建伴生对象的方式是在周围类中创建一个静态字段。它在静态范围内实例化(在实例化测试之前)。

这是在Java中反编译时的样子:

public final class SharedPreferencesHelper {
  public static final SharedPreferencesHelper.Companion Companion = new 
  SharedPreferencesHelper.Companion((DefaultConstructorMarker)null);
  // ...
}

为此,您必须使用模拟分配给定字段,而不是拦截 Companion 对象的创建。这甚至不需要使用 PowerMock 并且可以通过反射来完成:https://dzone.com/articles/how-to-change-private-static-final-fields

关于android - 使用 Powermock2 和 Kotlin 模拟静态类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50757653/

相关文章:

java - 如何模拟 Couchbase Lite 数据库

android - 使用自定义接收器测试 chromecast 应用程序?

android - 通过 Android 应用程序中的 Intent 打开没有地址栏的浏览器

java - Spring @Transactional 阻止 @Service 中的 @Autowired

java - 在 Android Studio(KOTLIN) 中使复选框的文本成为可点击的链接,从而打开某个网站?

java - IllegalArgumentException : width and height must be > 0 - error when loading an image using Imgcodecs. 已读

java - 更新 Android 上的选项卡?

java - 当我尝试在 Android Studio 中扩展 ImageView 时收到一条错误消息

java - arraylength 中操作数堆栈上的错误类型

java - 最终类中的 Powermock 链式方法调用