测试中无法正确初始化lateinit修饰的PrefManager类实例问题
问题分析
测试失败的核心原因是你把**测试类实例(PrefManagerTest)**传给了PrefManager的构造方法,但PrefManager需要的是Android系统的Context对象——测试类并非Context的子类,导致PrefManager内部的pref和editor都为null,调用setUsername和getUsername时完全没有操作到SharedPreferences,自然断言失败。
解决方案
下面提供两种可行的解决方式,根据你的测试场景选择:
方案1:使用Instrumented测试(真实设备/模拟器环境)
这种方式会在Android设备或模拟器上执行测试,可以获取真实的Application Context,适合需要验证真实SharedPreferences行为的场景。
步骤1:添加依赖(若未添加)
在app模块的build.gradle中补充:
androidTestImplementation 'androidx.test:core:1.5.0' androidTestImplementation 'androidx.test.ext:junit:1.1.5'
步骤2:修改测试代码
package com.example.testwork import androidx.test.core.app.ApplicationProvider import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import androidx.test.ext.junit.runners.AndroidJUnit4 import android.content.Context @RunWith(AndroidJUnit4::class) class PrefManagerTest { private lateinit var prefManager: PrefManager @Before fun setUp() { // 获取真实的Application Context val context = ApplicationProvider.getApplicationContext<Context>() prefManager = PrefManager(context) } @Test fun getUsername_ShouldReturnSavedValue() { val testName = "name" prefManager.setUsername(testName) assertEquals(testName, prefManager.getUsername()) } }
方案2:使用Mock框架(本地JVM单元测试)
用Mockito模拟Context、SharedPreferences和Editor,不需要依赖Android设备,测试执行速度更快,适合纯逻辑验证。
步骤1:添加依赖
在app模块的build.gradle中补充:
testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:4.11.0'
步骤2:修改测试代码
package com.example.testwork import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.mockito.Mock import org.mockito.Mockito.* import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule import android.content.Context import android.content.SharedPreferences class PrefManagerTest { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() @Mock private lateinit var mockContext: Context @Mock private lateinit var mockSharedPrefs: SharedPreferences @Mock private lateinit var mockEditor: SharedPreferences.Editor private lateinit var prefManager: PrefManager @Before fun setUp() { // 模拟Context获取SharedPreferences的行为 `when`(mockContext.getSharedPreferences(anyString(), anyInt())).thenReturn(mockSharedPrefs) // 模拟SharedPreferences获取Editor的行为 `when`(mockSharedPrefs.edit()).thenReturn(mockEditor) // 模拟Editor.commit()返回成功 `when`(mockEditor.commit()).thenReturn(true) prefManager = PrefManager(mockContext) } @Test fun getUsername_ShouldReturnSavedValue() { val testName = "name" // 模拟SharedPreferences返回设置的用户名 `when`(mockSharedPrefs.getString("username", "")).thenReturn(testName) prefManager.setUsername(testName) // 验证Editor是否执行了putString操作 verify(mockEditor).putString("username", testName) // 验证commit是否被调用 verify(mockEditor).commit() assertEquals(testName, prefManager.getUsername()) } }
额外优化建议
你的PrefManager还有一些可以优化的点,避免潜在问题:
- 将常量改为非可空的
const val,消除可空风险:private const val PREF_NAME = "SharedPreferences" private const val IS_LOGIN = "is_login" const val PRIVATE_MODE = 0 - 不要在类初始化时直接获取
pref和editor,改用构造方法初始化或懒加载,避免Context为空时的空指针:class PrefManager(context: Context) { private val pref: SharedPreferences = context.getSharedPreferences(PREF_NAME, PRIVATE_MODE) // 每次获取editor都用最新实例 private val editor: SharedPreferences.Editor get() = pref.edit() // ...其他方法 } - 建议用
editor.apply()代替commit(),apply()是异步提交,性能更优;若需同步判断提交结果再用commit()。
内容的提问来源于stack exchange,提问作者Another Pain
相关产品推荐
相关产品推荐

