AndroidTest中Mockito失效:lateinit属性未初始化问题求助
解决Mockito Lateinit属性未初始化问题
我来帮你一步步排查并解决这个Android Instrumented测试中的Mock注入问题:
问题根源分析
你遇到的kotlin.UninitializedPropertyAccessException: lateinit property storage has not been initialized错误,主要来自三个核心问题:
- Mockito注解未触发初始化:在
androidTest目录的Instrumented测试中,@Mock和@InjectMocks注解不会自动生效,必须手动初始化。 - 错误的
@InjectMocks使用场景:你把@InjectMocks标注在了ActivityTestRule实例上,这个Rule是用来管理Activity生命周期的,根本不应该被用来注入Mock对象。 - 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测试必须使用
AndroidJUnit4Runner,不能直接用Mockito的专属Runner。 - 一定要调用
MockitoAnnotations.initMocks(this),否则@Mock标注的变量永远是未初始化状态。 - 绝对不要给
ActivityTestRule加@InjectMocks,它的职责是管理Activity启动,和依赖注入无关。
内容的提问来源于stack exchange,提问作者Jono
相关产品推荐
相关产品推荐

