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

如何在Kotlin中强制模拟静态扩展方法的返回值?

问题原因

你遇到的核心问题是Kotlin扩展函数的本质是静态方法,Mockito常规的对象mock方式无法拦截静态方法调用:

  • 你定义的Job.toOtherJob()扩展函数,编译成字节码后其实是Mapper类中的静态方法toOtherJob(Job),只是Kotlin语法糖让它看起来像是Job的成员方法。
  • 你mock的是Job对象,但扩展函数不属于Job的成员,所以whenever(job.toOtherJob())本质是调用静态方法,Mockito无法拦截,自然会执行真实逻辑。
解决方案

方案1:修改源码(推荐)

把扩展函数改成可依赖注入的接口方法,彻底规避静态方法的测试问题:

  1. 定义Mapper接口:
interface JobMapper {
    fun Job.toOtherJob(): OtherJob.Job
}
  1. 实现默认Mapper:
class DefaultJobMapper : JobMapper {
    override fun Job.toOtherJob(): OtherJob.Job {
        // 原来的转换逻辑
    }
}
  1. 业务代码中注入JobMapper接口,测试时mock这个接口:
class ServiceTests {
    @Mock
    private lateinit var jobMapper: JobMapper

    @Mock
    private lateinit var job: Job

    @BeforeEach
    fun setUp() {
        MockitoAnnotations.openMocks(this)
    }

    @Test
    fun testJob() {
        val mockOtherJob = OtherJob.Job()
        // mock接口方法
        whenever(jobMapper.toOtherJob(job)).thenReturn(mockOtherJob)

        // 业务代码中通过jobMapper调用转换
        val result = jobMapper.toOtherJob(job)
        assertEquals(mockOtherJob, result)
    }
}

方案2:静态Mock(不推荐,仅当无法修改源码时用)

使用Mockito的静态mock功能(需要Mockito 3.4.0+,并引入mockito-inline依赖):

  1. 确保依赖中包含mockito-inline(Gradle示例):
testImplementation "org.mockito:mockito-inline:4.+"
  1. 修改测试代码,静态mockMapper类:
class ServiceTests {
    @Mock
    private lateinit var job: Job

    @BeforeEach
    fun setUp() {
        MockitoAnnotations.openMocks(this)
    }

    @Test
    fun testJob() {
        val mockOtherJob = OtherJob.Job()
        // 静态mock Mapper类,作用域在try-with-resources内
        mockStatic(Mapper::class.java).use { mockedMapper ->
            // 指定静态方法的返回值
            mockedMapper.`when`<OtherJob.Job> { job.toOtherJob() }
                .thenReturn(mockOtherJob)

            // 调用转换方法,此时会返回mock值
            val result = job.toOtherJob()
            assertEquals(mockOtherJob, result)
        }
    }
}
测试代码的额外优化点
  • 把@BeforeAll/@AfterAll改成@BeforeEach/@AfterEach,因为@BeforeAll是静态方法,不适合初始化非静态的mock对象
  • 不要把mock对象声明为nullable(val job: Job? = null),Mockito会生成非空的代理对象,直接用lateinit var job: Job即可

内容的提问来源于stack exchange,提问作者AzzamAziz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 15:00:43