如何在Kotlin中用Mockito mock类私有成员并测试方法调用
解决Employee类中promote方法调用的JUnit测试问题
方案一:重构代码(推荐)
直接调整Employee类的设计,让依赖和状态更易于测试,避免依赖反射操作私有变量:
class Employee constructor( employeeName: String?, // 注入PromotedPosition依赖,测试时可直接传入mock对象 val promotedPosition: PromotedPosition? = null ) { var isEligibleForPromotion = false // 改为可外部访问的变量,或提供setter方法 init { try { // 原有的晋升资格检查逻辑 } catch(e: Exception) {} } fun givePromotion(employeeName: String?) { if(isEligibleForPromotion) { promotedPosition?.promote(employeeName) } } }
对应的JUnit测试用例(基于Mockito + JUnit 5):
import org.junit.jupiter.api.Test import org.mockito.Mockito.* class EmployeeTest { @Test fun `givePromotion 应在符合资格时调用promote方法`() { // 1. Mock PromotedPosition实例 val mockPromotedPosition = mock(PromotedPosition::class.java) // 2. 创建Employee实例并注入mock对象 val employee = Employee("testName", mockPromotedPosition) // 3. 设置晋升资格为true employee.isEligibleForPromotion = true // 执行目标方法 employee.givePromotion("testName") // 验证promote方法是否被正确调用 verify(mockPromotedPosition).promote("testName") } @Test fun `givePromotion 不应在不符合资格时调用promote方法`() { val mockPromotedPosition = mock(PromotedPosition::class.java) val employee = Employee("testName", mockPromotedPosition) employee.isEligibleForPromotion = false employee.givePromotion("testName") // 验证promote方法从未被调用 verify(mockPromotedPosition, never()).promote(anyString()) } }
方案二:不修改原代码,用反射控制私有变量
如果无法调整原Employee类的代码,可以通过反射工具修改私有变量的取值:
使用Spring的ReflectionTestUtils工具
import org.junit.jupiter.api.Test import org.mockito.Mockito.* import org.springframework.test.util.ReflectionTestUtils class EmployeeTest { @Test fun `通过反射设置资格为true时,givePromotion应调用promote`() { val mockPromotedPosition = mock(PromotedPosition::class.java) val employee = Employee("testName") // 修改私有变量isEligibleForPromotion为true ReflectionTestUtils.setField(employee, "isEligibleForPromotion", true) // 为私有变量promotedPosition注入mock对象 ReflectionTestUtils.setField(employee, "promotedPosition", mockPromotedPosition) employee.givePromotion("testName") verify(mockPromotedPosition).promote("testName") } @Test fun `通过反射设置资格为false时,givePromotion不应调用promote`() { val mockPromotedPosition = mock(PromotedPosition::class.java) val employee = Employee("testName") ReflectionTestUtils.setField(employee, "isEligibleForPromotion", false) ReflectionTestUtils.setField(employee, "promotedPosition", mockPromotedPosition) employee.givePromotion("testName") verify(mockPromotedPosition, never()).promote(anyString()) } }
使用Kotlin原生反射
import org.junit.jupiter.api.Test import org.mockito.Mockito.* import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.jvm.isAccessible class EmployeeTest { @Test fun `通过Kotlin反射设置资格为true时,givePromotion应调用promote`() { val mockPromotedPosition = mock(PromotedPosition::class.java) val employee = Employee("testName") // 修改私有变量isEligibleForPromotion的值 val eligibleProp = Employee::class.declaredMemberProperties.find { it.name == "isEligibleForPromotion" }!! eligibleProp.isAccessible = true eligibleProp.setter.call(employee, true) // 为私有变量promotedPosition注入mock对象 val positionProp = Employee::class.declaredMemberProperties.find { it.name == "promotedPosition" }!! positionProp.isAccessible = true positionProp.setter.call(employee, mockPromotedPosition) employee.givePromotion("testName") verify(mockPromotedPosition).promote("testName") } }
注意事项
- 优先选择方案一,重构后的代码更符合面向对象设计原则,测试逻辑清晰且维护成本低。
- 反射方案属于“侵入式”测试,一旦类中私有变量名称修改,测试会直接失败,后续维护成本较高。
内容的提问来源于stack exchange,提问作者Coding_yatra
相关产品推荐
相关产品推荐

