Jorge Domínguez
01/27/2023, 5:49 PMUninitializedPropertyAccessException
, according the the stacktrace the problem traces back to this java.lang.AbstractMethodError: abstract method "boolean kotlin.reflect.KClass.isValue()"
which seems to be related to mockK, has anybody come across this issue before?Adam S
01/27/2023, 8:26 PM.isValue()
might be related to value classes. Are you using value classes? What versions of Kotlin and MockK are you using?Jorge Domínguez
01/27/2023, 8:31 PMio.mockk.core.ValueClassSupport.isValue_safe(ValueClassSupport.kt:64)
, and during our inspection decided to downgrade mockK from 1.13.1
to 1.12.0
and suddently our test suit succeeds completelly, so we're looking into what could've changed between these two versions. Oh and we're using Kotlin 1.7.20
Adam S
01/27/2023, 8:38 PMJorge Domínguez
01/27/2023, 8:55 PM01-27 17:30:59.374 32268 32287 E TestRunner: ----- begin exception -----
01-27 17:30:59.376 32268 32287 E TestRunner: java.lang.AbstractMethodError: abstract method "boolean kotlin.reflect.KClass.isValue()"
01-27 17:30:59.376 32268 32287 E TestRunner: at io.mockk.core.ValueClassSupport.isValue_safe(ValueClassSupport.kt:64)
01-27 17:30:59.376 32268 32287 E TestRunner: at io.mockk.core.ValueClassSupport.getBoxedClass(ValueClassSupport.kt:33)
01-27 17:30:59.376 32268 32287 E TestRunner: at io.mockk.impl.instantiation.JvmMockFactoryHelper.toDescription$mockk(JvmMockFactoryHelper.kt:156)
01-27 17:30:59.376 32268 32287 E TestRunner: at io.mockk.impl.instantiation.JvmMockFactoryHelper$mockHandler$1.invocation(JvmMockFactoryHelper.kt:26)
01-27 17:30:59.376 32268 32287 E TestRunner: at io.mockk.proxy.android.advice.Advice.handle$lambda-0(Advice.kt:78)
01-27 17:30:59.376 32268 32287 E TestRunner: at io.mockk.proxy.android.advice.Advice.$r8$lambda$BJwd2E_KuGTkZCQEA_otcmNp7lY(Unknown Source:0)
01-27 17:30:59.376 32268 32287 E TestRunner: at io.mockk.proxy.android.advice.Advice$$ExternalSyntheticLambda0.call(Unknown Source:10)
Adam S
01/27/2023, 9:00 PMJorge Domínguez
01/27/2023, 9:29 PMKClass.isValue
can throw an exception. The question is why does it happen thenJozsef Kiraly
01/31/2023, 10:41 AMobject
instance, which gives us access to a koinInstance
, provides methods to initialise and release our Koin instance, and create specific scopes within.
We also rely on Koin for a handful of message types the library’s communication is built around (e.g. we use a relative timestamp, which requires a TimestampProvider, which is injected via Koin).
To unit/integration test these, we’ve created a custom TestRule
that, during evaluation, mocks the whole. aforementioned object
, and forces it to return a custom Koin instance we can test with.
This object mocking, and the custom Koin instance return is what causes the above error.
A somewhat simplified version of the setup we have is:
object Di {
private var koinInstance: Koin? = null
fun init(context: Context, vararg modules: Module): Koin {
// Koin init here
}
fun koin() = koinInstance?.let { it } ?: throw CustomException("We handle de-init of DI here")
fun release()
}
fun DISupportRule : TestRule {
// ... MockK'd definitions of providers we need
val stuffProvider = mockk<StuffProvider>(relaxed = true)
private val module = module {
single { stuffProvider }
}
private val testKoinInstance = koinApplication {
modules(module)
}
// This is the method that causes the above crash
private fun setup() {
mockkObject(Di)
every { Di.koin() } returns testKoinInstance
}
private fun teardown() {
unmockkobject(Di)
}
override fun apply(...): Statement {
return object : Statement() {
override fun evaluate() {
setup()
try { base.evaluate() } finally { teardown() }
}
}
}
}
Adam S
01/31/2023, 10:50 AMUninitializedPropertyAccessException
). Or possibly Kotlin Reflect isn’t fully available, so when MockK tries to check for a value class, it fails.Jozsef Kiraly
01/31/2023, 10:54 AMinit()
method, there’s no further clarity from MockK where it is actually coming.
Our init method is also pretty straightforward:
if (koinInstance == null) {
koinInstance = koinApplication {
androidContext(context.applicationContext)
modules(modules.toList())
}.koin
koinInstance?.createScope(OUR_CUSTOM_SCOPE_ID, OUR_CUSTOM_SCOPE_NAME)
}
return koin()
KClass.isValue()
implementation is the breaking change.Adam S
01/31/2023, 11:01 AMDi::class.isValue
?Jozsef Kiraly
01/31/2023, 12:04 PM