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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 13:40:25