如何不使用@Mock注解mock Android Context?遇Looper方法未mock异常
你的怀疑不正确,用mock<Context>和@Mock注解创建Mock对象的本质是一样的——底层都是依托Mockito实现的,两者没有能力上的差异,报错的原因和创建Mock的方式无关。
报错原因
这个异常是因为Android SDK中的很多核心类(比如Looper、Context的部分方法)在本地JVM单元测试环境下没有真实的实现。当你的测试代码(或被测试代码)调用到Context的getMainLooper()这类方法时,Mockito默认只会Mock你显式指定的方法,对于未Stub的方法,默认行为会抛出“方法未Mock”的异常,和你用mock<Context>还是@Mock没有关系。
之前用@Mock lateinit var context: Context能正常工作,大概率是因为你配合了Mockito的初始化机制(比如MockitoAnnotations.initMocks(this)、@RunWith(MockitoJUnitRunner::class)或者@ExtendWith(MockitoExtension::class)),但这只是初始化Mock的方式不同,不是@Mock本身解决了Android方法未Mock的问题。
解决办法
有三种常见的处理方式:
配置Gradle让未Mock的方法返回默认值
在你的App模块的build.gradle(或build.gradle.kts)中添加如下配置,让所有未被Stub的Android方法返回默认值(比如null、0、空集合等),避免抛出异常:android { testOptions { unitTests { returnDefaultValues = true } } }手动Stub需要的方法
在创建Context的Mock对象时,显式Stub那些被调用到的Android系统方法,比如:private val context = mock<Context> { // 针对getMainLooper方法返回一个可用的Looper实例 on { mainLooper } doReturn Looper.getMainLooper() // 如果还有其他被调用的方法,也可以在这里Stub }或者你可以设置默认回答,让所有未Stub的方法都返回默认值:
private val context = mock<Context>(defaultAnswer = ReturnsEmptyValues())使用Robolectric
Robolectric会在本地JVM环境中模拟完整的Android系统,提供Context等Android类的真实实现,无需手动Mock这些系统方法,就能让测试代码正常调用Android相关API。
内容的提问来源于stack exchange,提问作者gureisu

