You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单元测试中getSharedPreferences返回null?Kotlin场景下的问题求助

解决SharedPreferenceHandler测试时的空指针问题

首先直接回答你的核心问题:完全可以在测试类中通过SharedPreferenceHandler.mInstance.accessToken!!获取accessToken,但你当前的实现因为依赖未初始化的Application单例,导致测试时抛出异常,需要先重构代码来解决这个问题。

问题根源

你的SharedPreferenceHandler单例在类加载阶段就会初始化,而初始化过程依赖Application.mInstance——在Android测试环境中,Application对象不会自动初始化,所以Application.mInstance是null,调用getSharedPreferences时自然会抛出IllegalStateException。

重构SharedPreferenceHandler,适配测试场景

我们需要修改单例的实现,消除对Application单例的硬依赖,同时允许测试时注入自定义的SharedPreferences实例:

class SharedPreferenceHandler(private val sharedPreferences: SharedPreferences) {
    companion object {
        @Volatile
        private var mInstance: SharedPreferenceHandler? = null

        // 允许传入SharedPreferences,测试时可以传Mock对象
        fun getInstance(sharedPrefs: SharedPreferences? = null): SharedPreferenceHandler {
            return mInstance ?: synchronized(this) {
                val prefs = sharedPrefs ?: run {
                    checkNotNull(Application.mInstance) {
                        "Application instance must be initialized first!"
                    }.getSharedPreferences("myApp", Context.MODE_PRIVATE)
                }
                SharedPreferenceHandler(prefs).also { mInstance = it }
            }
        }
    }

    var accessToken: String?
        get() = sharedPreferences.getString(SharedPreference.ACCESS_TOKEN.name, null)
        set(token) = sharedPreferences.edit()
            .putString(SharedPreference.ACCESS_TOKEN.name, token)
            .apply()
}

调整测试代码,注入Mock的SharedPreferences

用Mockito创建Mock的SharedPreferences,然后通过getInstance注入,这样就能控制accessToken的返回值,同时避免依赖Application:

@Test
public void reload() {
    // 创建Mock的SharedPreferences
    SharedPreferences mockPrefs = Mockito.mock(SharedPreferences.class);
    // 预设accessToken的返回值
    when(mockPrefs.getString(SharedPreference.ACCESS_TOKEN.name, null))
        .thenReturn("234234234234234");
    
    // 初始化单例时传入Mock对象
    SharedPreferenceHandler.getInstance(mockPrefs);
    
    // 执行测试方法
    presenter.reload(viewModel);
    
    // 此时你可以直接获取accessToken
    String token = SharedPreferenceHandler.getInstance().accessToken;
    assertNotNull(token);
    assertEquals("234234234234234", token);
}

额外说明

如果你的测试框架是JUnit 5或者用了AndroidX Test,也可以考虑用ApplicationProvider.getApplicationContext()来获取测试环境的Context,再创建真实的SharedPreferences实例,但Mock方式更灵活,适合单元测试场景。

内容的提问来源于stack exchange,提问作者azsoftco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:56:48