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

AndroidTest中Mockito失效:lateinit属性未初始化问题求助

解决Mockito Lateinit属性未初始化问题

我来帮你一步步排查并解决这个Android Instrumented测试中的Mock注入问题:

问题根源分析

你遇到的kotlin.UninitializedPropertyAccessException: lateinit property storage has not been initialized错误,主要来自三个核心问题:

  1. Mockito注解未触发初始化:在androidTest目录的Instrumented测试中,@Mock和@InjectMocks注解不会自动生效,必须手动初始化。
  2. 错误的@InjectMocks使用场景:你把@InjectMocks标注在了ActivityTestRule实例上,这个Rule是用来管理Activity生命周期的,根本不应该被用来注入Mock对象。
  3. Presenter未正确注入到Activity:即使你Mock了Presenter,Activity启动时大概率还是创建了真实的Presenter实例,导致内部的storage属性完全没被Mock填充。

解决方案

1. 修正测试类的注解与初始化逻辑

先修改测试类,补上必要的Runner和初始化步骤:

@RunWith(AndroidJUnit4::class)
class LaunchActivityTest {

    @Mock
    lateinit var storage: StorageContract

    @Mock
    lateinit var activityCallBack: LaunchContract.ActivityCallBack

    @InjectMocks
    lateinit var launchPresenter: LaunchContract.Presenter

    @get:Rule
    var activityRule = ActivityTestRule<LaunchActivity>(LaunchActivity::class.java, false, false)

    @Before
    fun setup() {
        // 手动初始化Mockito注解,让@Mock和@InjectMocks生效
        MockitoAnnotations.initMocks(this)
    }

    @Test
    fun testRegAndLoginVisible() {
        // Mock storage的返回值
        Mockito.`when`(storage.isLoggedIn()).thenReturn(false)
        
        // 先获取未启动的Activity实例,注入Mock Presenter
        val activity = activityRule.activity
        activity.setPresenter(launchPresenter)
        
        // 启动Activity
        activityRule.launchActivity(null)

        onView(withId(R.id.loginBtn))
            .check(matches(isDisplayed()))
        onView(withId(R.id.registerBtn))
            .check(matches(isDisplayed()))
    }
}

2. 给Activity添加Presenter注入入口

为了让测试能替换Activity内部的Presenter,建议在LaunchActivity中添加一个公开方法,方便测试时注入Mock对象:

class LaunchActivity : AppCompatActivity(), LaunchContract.View {
    private lateinit var presenter: LaunchContract.Presenter

    // 测试专用:允许外部设置Presenter
    fun setPresenter(presenter: LaunchContract.Presenter) {
        this.presenter = presenter
        presenter.attachView(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_launch)
        // 如果没有外部注入Presenter,就创建默认实例
        if (!::presenter.isInitialized) {
            presenter = LaunchPresenter(StorageImpl(), this)
        }
    }
}

3. 优化构建脚本依赖

你的构建脚本同时引入了单元测试和Instrumented测试的Mockito依赖,建议明确区分避免冲突:

// 单元测试(test目录)依赖
testImplementation 'junit:junit:4.12'
testImplementation 'au.com.dius:pact-jvm-consumer-junit_2.11:3.5.10'
testImplementation 'org.mockito:mockito-inline:2.13.0'
testImplementation 'io.mockk:mockk:1.6.3'
testImplementation 'org.assertj:assertj-core:3.8.0'

// Instrumented测试(androidTest目录)依赖
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'junit:junit:4.12'
androidTestImplementation 'org.mockito:mockito-android:2.13.0'

关键注意点

  • Android Instrumented测试必须使用AndroidJUnit4 Runner,不能直接用Mockito的专属Runner。
  • 一定要调用MockitoAnnotations.initMocks(this),否则@Mock标注的变量永远是未初始化状态。
  • 绝对不要给ActivityTestRule加@InjectMocks,它的职责是管理Activity启动,和依赖注入无关。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:05:03