diff --git a/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt b/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt index 961634ead..5f6ca43c4 100644 --- a/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt +++ b/agent/android/src/main/kotlin/io/mockk/ValueClassSupport.kt @@ -3,8 +3,8 @@ package io.mockk import kotlin.reflect.KClass import kotlin.reflect.KProperty1 import kotlin.reflect.full.declaredMemberProperties -import kotlin.reflect.full.primaryConstructor import kotlin.reflect.jvm.isAccessible +import kotlin.reflect.jvm.javaField // TODO this class is copy-pasted and should be de-duplicated // see https://github.com/mockk/mockk/issues/857 @@ -45,10 +45,10 @@ private val KClass.boxedProperty: KProperty1 get() = if (!this.isValue_safe) { throw UnsupportedOperationException("$this is not a value class") } else { - // value classes always have exactly one property @Suppress("UNCHECKED_CAST") valueClassFieldCache.getOrPut(this) { - this.declaredMemberProperties.first().apply { isAccessible = true } + // value classes always have exactly one property with a backing field + this.declaredMemberProperties.first { it.javaField != null }.apply { isAccessible = true } } as KProperty1 } diff --git a/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt b/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt index 961634ead..5f6ca43c4 100644 --- a/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt +++ b/agent/jvm/src/main/kotlin/io/mockk/ValueClassSupport.kt @@ -3,8 +3,8 @@ package io.mockk import kotlin.reflect.KClass import kotlin.reflect.KProperty1 import kotlin.reflect.full.declaredMemberProperties -import kotlin.reflect.full.primaryConstructor import kotlin.reflect.jvm.isAccessible +import kotlin.reflect.jvm.javaField // TODO this class is copy-pasted and should be de-duplicated // see https://github.com/mockk/mockk/issues/857 @@ -45,10 +45,10 @@ private val KClass.boxedProperty: KProperty1 get() = if (!this.isValue_safe) { throw UnsupportedOperationException("$this is not a value class") } else { - // value classes always have exactly one property @Suppress("UNCHECKED_CAST") valueClassFieldCache.getOrPut(this) { - this.declaredMemberProperties.first().apply { isAccessible = true } + // value classes always have exactly one property with a backing field + this.declaredMemberProperties.first { it.javaField != null }.apply { isAccessible = true } } as KProperty1 } diff --git a/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupportDsl.kt b/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupportDsl.kt index 650c7aeca..9ad287209 100644 --- a/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupportDsl.kt +++ b/dsl/jvm/src/main/kotlin/io/mockk/ValueClassSupportDsl.kt @@ -4,6 +4,7 @@ import kotlin.reflect.KClass import kotlin.reflect.KProperty1 import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.jvm.isAccessible +import kotlin.reflect.jvm.javaField /** * Provides value class support in the `mockk-dsl-jvm` subproject. @@ -50,10 +51,10 @@ internal object ValueClassSupportDsl { get() = if (!this.isValue_safe) { throw UnsupportedOperationException("$this is not a value class") } else { - // value classes always have exactly one property @Suppress("UNCHECKED_CAST") valueClassFieldCache.getOrPut(this) { - this.declaredMemberProperties.first().apply { isAccessible = true } + // value classes always have exactly one property with a backing field + this.declaredMemberProperties.first { it.javaField != null }.apply { isAccessible = true } } as KProperty1 } diff --git a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt index b95d04231..84a74d3bd 100644 --- a/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt +++ b/mockk/common/src/test/kotlin/io/mockk/it/ValueClassTest.kt @@ -476,13 +476,27 @@ class ValueClassTest { assertEquals("example", result) } - // - // + + @Test + fun `result value`() { + val givenResult = DummyValue(42) + + val mock = mockk { + every { returnValueClass() } returns givenResult + } + + val result = mock.returnValueClass() + + assertEquals(givenResult, result) + } companion object { @JvmInline - value class DummyValue(val value: Int) + value class DummyValue(val value: Int) { + // field without backing field + val text: String get() = value.toString() + } @JvmInline value class DummyValueWrapper(val value: DummyValue) @@ -501,6 +515,8 @@ class ValueClassTest { fun argValueClassReturnValueClass(valueClass: DummyValue): DummyValue = DummyValue(0) + fun returnValueClass(): DummyValue = + DummyValue(0) fun argNoneReturnsUInt(): UInt = 123u }