给定此代码:
// Subject.kt
open class Subject(var x: Int) {
constructor(): this(42) {
println("made it")
}
fun doit() {
x += 1
println("did it: $x")
}
}
// Tests.kt
import org.junit.jupiter.api.Test
import org.mockito.Mockito
class Tests {
@Test
fun makeit() {
val mock = Mockito.mock(Subject::class.java)
val details = Mockito.mockingDetails(mock)
println("Is mock: ${details.isMock}")
println("Is spy: ${details.isSpy}")
mock.doit()
mock.doit()
}
}
运行makeit
时,输出为:
Is mock: true
Is spy: false
did it: 1
did it: 2
这似乎表明正在创建主题的某个实例,但绕过了潜在的关键构造函数逻辑。这与"部分mock"是一致的,但代码没有做任何事情来请求这样的东西。
我发现令人惊讶的是,这是默认行为,因为文档都强烈警告不要使用部分mock。我一直找不到描述mock()
何时返回部分mock的文档,因此也不知道如何从类中获得"完整mock"。
因此:
Mockito.mock()
什么时候创建部分mock- Mockito能为一个类创建一个"完整的mock"吗?或者只是为了一个界面
- 如何申请"全模拟">
通过源代码和试错测试,我得出了以下结论:
- 当模拟一个类时,Mockito会创建一个ByteBuddy生成的类的子类的实例,而不调用构造函数=>所有成员数据都是默认值
- 开放方法(Java的默认方法;在Kotlin中用
open
声明(:- 默认情况下不调用,并返回返回类型的默认值
- 当配置CCD_ 5时将被调用
- 如果创建mock时
defaultAnswer
设置为CALLS_REAL_METHODS
,则将调用
final
方法不能被重写=>它们将被正常调用,但它们将看到所有成员数据的默认值
所以,似乎所有的类mock都是部分mock,但由于Java中默认的方法是打开的,所以它们通常看起来像常规mock。默认情况下,它们实际上是常规mock。
这在Kotlin中很快就会显示出来,因为默认情况下方法是final
。
知道这是如何工作的,处理类模拟就不会那么令人沮丧了!