使用Mockito mock final类内部方法时sCode返回null如何解决
问题背景
现有业务代码定义如下:
public final class PolicySummaryUtils { public PolicySummaryResponse.Policy getPolicy() { return getPolicySummaryResponse().accounts.get(0).policies.get(0); } } public static class Policy { @Expose public String sCode; @SerializedName("policySource") // 其余类成员省略 }
单元测试场景:需要在ViewModelTest中mock stateCode字段,已对policy对象做mock处理,但实际运行时sCode始终返回null,测试代码如下:
@ExperimentalCoroutinesApi @RunWith(MockitoJUnitRunner::class) class ViewModelTest{ @Mock lateinit var summaryUtils: PolicySummaryUtils @Mock lateinit var policy: Policy @Test fun payBill_return_acceptedPayBillResponse(){ return runTest { Mockito.when(summaryUtils.policyNumber).thenReturn(MOCK_POLICY_NO) Mockito.when(summaryUtils.policy).thenReturn(policy) Mockito.when(policy.stateCode).thenReturn(MOCK_STATE_CODE) } } }
核心疑问:
- 该场景下是否可以成功mock final类内部的方法?
- 当前sCode返回null的问题该如何修复?
问题解答
final类方法Mock可行性
原生Mockito默认不支持mock final类、final方法,满足以下任一条件即可正常mock:
- 使用Mockito 2及以上版本,在测试资源目录
src/test/resources/mockito-extensions/下新建org.mockito.plugins.MockMaker文件,文件内容填写mock-maker-inline,开启inline mock maker能力 - 使用Mockito 4及以上版本,部分发行版已默认开启final类mock支持
- 引入PowerMock等扩展测试框架实现final类mock
从当前代码能正常编写summaryUtils.policy的mock逻辑来看,final类mock的配置本身是生效的,这不是sCode返回null的根因。
sCode返回null的根因与修复
根因
问题出在两个编码错误:
- 字段/属性名不匹配:定义的Policy类中公开字段名为
sCode,测试代码中mock的是stateCode属性,Kotlin会将属性访问映射为对应getter方法调用,类中不存在getStateCode()方法时,mock逻辑根本不会绑定到目标字段上。 - 错误mock公开字段:Mockito的
when().thenReturn()逻辑针对的是方法调用拦截,Java的public字段是直接内存访问,不存在代理拦截点,mock生成的代理对象中所有字段默认都是类型初始值,引用类型固定为null,直接对字段做mock不会生效。
修复方案
最优实现是不要mock简单值对象,直接实例化真实Policy对象赋值即可,参考代码如下:
@ExperimentalCoroutinesApi @RunWith(MockitoJUnitRunner::class) class ViewModelTest{ private val MOCK_POLICY_NO = "TEST_POLICY_001" private val MOCK_STATE_CODE = "TEST_STATE_001" @Mock lateinit var summaryUtils: PolicySummaryUtils private lateinit var policy: Policy @Before fun initTestData() { policy = Policy() // public字段直接赋值即可,不需要走mock逻辑 policy.sCode = MOCK_STATE_CODE } @Test fun payBill_return_acceptedPayBillResponse() = runTest { // Kotlin中调用Java getter方法时需要加反引号避免和关键字冲突 Mockito.`when`(summaryUtils.policyNumber).thenReturn(MOCK_POLICY_NO) Mockito.`when`(summaryUtils.policy).thenReturn(policy) // 编写后续测试断言逻辑 } }
补充说明:如果Policy类实际存在
getStateCode()方法、sCode只是序列化对应的字段名,需要检查对应getter方法是否被final修饰、字段访问权限是否正确,确保Mockito能拦截到getter调用,这种场景下才可以使用Mockito.when(policy.stateCode).thenReturn(MOCK_STATE_CODE)的写法。
内容的提问来源于stack exchange,提问作者Abraham Mathew
相关产品推荐
相关产品推荐

