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

测试中无法正确初始化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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 01:31:04