在Mockito中模拟带访问器的Kotlin属性:SharedPreferences报错解决
这个问题我在测试自己的Android应用时也踩过坑——本质是本地单元测试环境下,Android SDK里的很多系统类都是空实现(未Mock),PreferenceManager.getDefaultSharedPreferences()就是典型代表。下面给你几个实用的解决方案,按推荐程度排序:
方案1:重构代码,提升可测试性(最推荐)
直接硬编码调用PreferenceManager会让代码耦合度太高,不利于测试。我们可以把获取SharedPreferences的逻辑抽离出来,方便测试时替换:
第一步:修改你的App类
class YourAppClass(private val context: Context) { // 抽离SP获取逻辑,用open修饰方便测试时重写 protected open fun provideSharedPreferences(): SharedPreferences { return PreferenceManager.getDefaultSharedPreferences(context) } // token属性依赖抽离后的方法 val token: String? get() = provideSharedPreferences().getString("token_key", null) }
第二步:编写测试类
创建一个测试用的子类,重写provideSharedPreferences方法返回Mock的SP实例:
import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnitRunner import android.content.SharedPreferences @RunWith(MockitoJUnitRunner::class) class YourAppClassTest { @Mock lateinit var mockSharedPreferences: SharedPreferences @Test fun token_shouldReturnMockValue() { // Mock SP的getString方法,返回你需要的模拟token `when`(mockSharedPreferences.getString("token_key", null)).thenReturn("mock_test_token_123") // 创建测试用的App子类,注入Mock的SP val testableApp = object : YourAppClass(mockContext) { override fun provideSharedPreferences(): SharedPreferences { return mockSharedPreferences } } // 验证token属性返回预期值 assert(testableApp.token == "mock_test_token_123") } }
这种方式不需要依赖静态Mock,代码结构更清晰,后续维护也更方便。
方案2:用Mockito-inline Mock静态方法
如果你不想重构代码,可以用Mockito-inline来MockPreferenceManager的静态方法。注意需要先添加依赖:
在你的build.gradle(Module级)中添加:
testImplementation "org.mockito:mockito-core:4.11.0" testImplementation "org.mockito:mockito-inline:4.11.0" // 必须加这个才能Mock静态方法
然后编写测试代码:
import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnitRunner import org.mockito.Mockito.mockStatic import android.preference.PreferenceManager import android.content.Context import android.content.SharedPreferences @RunWith(MockitoJUnitRunner::class) class YourAppClassTest { @Mock lateinit var mockContext: Context @Mock lateinit var mockSharedPreferences: SharedPreferences @Test fun token_shouldReturnMockValue() { // Mock PreferenceManager的静态方法,返回我们的Mock SP mockStatic(PreferenceManager::class.java).use { mockedStatic -> mockedStatic.`when` { PreferenceManager.getDefaultSharedPreferences(mockContext) }.thenReturn(mockSharedPreferences) // Mock SP的getString方法 `when`(mockSharedPreferences.getString("token_key", null)).thenReturn("mock_test_token_123") // 初始化App类并验证结果 val app = YourAppClass(mockContext) assert(app.token == "mock_test_token_123") } } }
mockStatic的use块会自动管理静态Mock的生命周期,避免影响其他测试用例。
方案3:使用AndroidX Test的MockSharedPreferences
AndroidX Test库提供了一个真实的SharedPreferences假实现,适合需要模拟复杂SP操作的场景。
首先添加依赖:
testImplementation "androidx.test:core:1.5.0" testImplementation "androidx.test.ext:junit:1.1.5"
然后编写测试代码:
import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.Test import org.junit.runner.RunWith import androidx.test.core.content.edit @RunWith(AndroidJUnit4::class) class YourAppClassTest { @Test fun token_shouldReturnMockValue() { // 获取测试用的Context,创建Mock SP val testContext = ApplicationProvider.getApplicationContext<Context>() val mockSp = androidx.test.core.content.SharedPreferencesPreferences(testContext, "test_sp") // 存入模拟的token值 mockSp.edit { putString("token_key", "mock_test_token_123") } // 这里需要让你的App类使用这个Mock SP,推荐用依赖注入的方式传入 // 比如修改App类构造,允许传入SP实例: // val app = YourAppClass(testContext, mockSp) assert(app.token == "mock_test_token_123") } }
这个方案更贴近真实的SP行为,但需要你的App类支持依赖注入SP实例。
内容的提问来源于stack exchange,提问作者saiedmomen
相关产品推荐
相关产品推荐

