Издеваться над частной собственностью
Допустим, у нас есть такой класс:
class Whatever {
private var something = false
fun aMethod(): Int {
return if( something ) {
1
} else {
0
}
}
}
Согласно документации, похоже, я должен быть в состоянии сделать следующее:
val classUnderTest = spyk(Whatever())
every { classUnderTest getProperty "something" } returns true
assertThat(classUnderTest.aMethod()).isEqualTo(1)
но вместо этого я получаю ошибку: io.mockk.MockKException: Missing calls inside every { ... } block
Я пользуюсь mockk 1.8.5, kotlin 1.2.51
4 ответа
Попробуйте использовать ответы вместо возвратов, например так:
val mock = spyk(MockCls(), recordPrivateCalls = true)
every { mock.property } answers { fieldValue }
every { mock getProperty "property" } propertyType Int::class answers { fieldValue + 6 }
every { mock setProperty "property" value any<Int>() } propertyType Int::class answers { fieldValue += value }
every {
mock.property = any()
} propertyType Int::class answers {
fieldValue = value + 1
} andThen {
fieldValue = value - 1
}
Вы можете использовать это небольшое расширение
fun Any.mockPrivateFields(vararg mocks: Any): Any {
mocks.forEach { mock ->
javaClass.declaredFields
.filter { it.modifiers.and(Modifier.PRIVATE) > 0 || it.modifiers.and(Modifier.PROTECTED) > 0 }
.firstOrNull { it.type == mock.javaClass}
?.also { it.isAccessible = true }
?.set(this, mock)
}
return this
}
он устанавливает значение первого поля того же типа, переданного в качестве параметра, и может использовать несколько параметров, например:
myObject.mockPrivateFields(1, "hi!")
он будет высмеивать первое найденное поле Int и первое найденное поле String.
Это только для частных / защищенных полей.
Вы можете изменить его для поддержки нескольких полей одного типа. Я не пробовал это с дженериками, но, думаю, это не сложно поддержать.
Разрабатывая ответ @Ignacio:
inline fun <reified T> T.setPrivateField(field: String, value: Any): T = apply {
T::class.java.declaredFields
.find { it.name == field}
?.also { it.isAccessible = true }
?.set(this, value)
}
Обратите внимание, что технически это не «издевательство», вы фактически устанавливаете значение в экземпляре, а не перехватываете его вызов.
Это происходит, вероятно, потому, что для вашего
something
свойство компилятором kotlin.
Что, если вы изменили его, чтобы он выглядел следующим образом?
class Whatever {
private val something
get() = false
fun aMethod(): Int {
return if( something ) {
1
} else {
0
}
}
}