InjectMocks 不适用于具有默认值的 Kotlin 构造函数参数

Posted

技术标签:

【中文标题】InjectMocks 不适用于具有默认值的 Kotlin 构造函数参数【英文标题】:InjectMocks doesn't work with Kotlin constructor arguments with default values 【发布时间】:2021-10-26 04:47:31 【问题描述】:

编辑:我用 mockito-kotlin here 创建了一张票

我有一个这样定义的类:

package me.jpalacios.poc

class MyClass(
    private val myDependency: MyDependency = MyDependency()
) 
    fun run() 
        myDependency.doSomething()
    

package me.jpalacios.poc

class MyDependency 

    fun doSomething() 
        println("I did something")
    

package me.jpalacios.poc

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.verify

@ExtendWith(MockitoExtension::class)
class MyClassTest 

    @Mock
    private lateinit var myDependency: MyDependency
    @InjectMocks
    private lateinit var myClass: MyClass

    @Test
    fun `Test InjectMocks`() 
        myClass.run()

        verify(myDependency).doSomething()
    

看起来像这样定义的测试不起作用,因为没有注入模拟:

@ExtendWith(MockitoExtension::class)
class MyClassTest 
    @Mock
    private lateinit var dependency: MyDependency
    @InjectMocks
    private lateinit var underTest: MyClass

plugins 
    kotlin("jvm") version "1.5.20"


group = "me.jpalacios"
version = "1.0-SNAPSHOT"

repositories 
    mavenCentral()


dependencies 
    implementation(kotlin("stdlib"))

    testImplementation("org.junit.jupiter:junit-jupiter:5.7.2")
    testImplementation("org.junit.jupiter:junit-jupiter-params:5.7.2")
    testImplementation("org.assertj:assertj-core:3.20.2")
    testImplementation("org.mockito.kotlin:mockito-kotlin:3.2.0")
    testImplementation("org.mockito:mockito-junit-jupiter:3.11.2")


tasks
    jar 
        duplicatesStrategy = DuplicatesStrategy.EXCLUDE

        configurations["compileClasspath"].forEach  file: File ->
            from(zipTree(file.absoluteFile))
        
    
    compileKotlin 
        kotlinOptions 
            jvmTarget = "$JavaVersion.VERSION_11"
        
    
    test 
        useJUnitPlatform()
    

有什么想法吗?

输出是:

我做了什么

需要但未调用:myDependency.doSomething(); -> 在 me.jpalacios.poc.MyDependency.doSomething(MyDependency.kt:6) 实际上,与此模拟的交互为零。

需要但未调用:myDependency.doSomething(); -> 在 me.jpalacios.poc.MyDependency.doSomething(MyDependency.kt:6) 实际上,与此模拟的交互为零。

在 me.jpalacios.poc.MyDependency.doSomething(MyDependency.kt:6) 在 me.jpalacios.poc.MyClassTest.Test InjectMocks(MyClassTest.kt:22) 在 java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native 方法)在 java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ...

【问题讨论】:

您能否使用您正在使用的 Mockito 依赖项和版本更新您的答案?以及输出中的任何警告或错误。 @aSemy 添加了带有输出的完整代码示例 我真的不是 Mockito 方面的专家,但是为构造函数参数添加默认值实际上意味着您不需要传递依赖项,所以如果 Mockito 不尝试,我不会感到惊讶在这种情况下注入模拟。它确实看到了一个可用的无参数构造函数,那么为什么不使用它呢? 【参考方案1】:

我真的不是 Mockito 方面的专家,但是为构造函数参数添加默认值实际上意味着您不需要传递依赖项,所以如果 Mockito 不尝试在其中注入模拟,我不会感到惊讶案子。它确实看到了一个可用的无参数构造函数,所以你不想让它使用它吗?

无论如何,我什至不确定你是否需要这个。为什么不自己实例化被测类,然后在需要的地方通过 mock 呢?

@ExtendWith(MockitoExtension::class)
class MyClassTest 

    @Mock
    private lateinit var myDependency: MyDependency

    @Test
    fun `Test InjectMocks`() 
        MyClass(myDependency).run()

        verify(myDependency).doSomething()
    

如果您需要在多个测试之间共享这样的初始化,您可以使用惰性属性或@BeforeTest 方法来创建它。

【讨论】:

我不认为这是@Joffrey 的情况。调试我可以看到 Mockito 试图调用构造函数,问题是当它检查构造函数时,它发现比代码中声明的多出 2 个参数 它尝试调用哪个构造函数?这就是我的观点。在这种情况下,Kotlin 代码生成了 2 个构造函数,我的意思是,mockito 选择了无参数构造函数而不是另一个。 如果是这样,那将违反@InjectMocks 的约定:“构造函数注入;选择了最大的构造函数”参见javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/…跨度> @user3505258 嗯,那很好,我不知道那个规则。我仍然不明白为什么你需要 InjectMocks 在这里 ***.com/questions/53912047/…

以上是关于InjectMocks 不适用于具有默认值的 Kotlin 构造函数参数的主要内容,如果未能解决你的问题,请参考以下文章

不使用InjectMocks创建对象会导致httpClient出现问题

Azure Api 规则不适用于使用具有身份验证基本和身份验证证书的默认助手

谷歌表格中的条件格式不适用于重复项

订阅不适用于淘汰模型作为功能

Typeahead JS 不适用于具有相同类的多个输入

parse_dates 不适用于默认日期时间格式