Издеваться над частной собственностью

Допустим, у нас есть такой класс:

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
        }
    }
}
Другие вопросы по тегам