Search code examples
kotlinspring-boot-testmockk

What is the reason for an UninitializedPropertyAccessException in @SpringBootTest with MockK and Kotlin?


I am trying to use MockK 1.10.2 with Kotlin 1.4.10 and @SpringBootTest (Spring Boot 2.2.2.RELEASE) and I can't get it run because of a

OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot     loader classes because bootstrap classpath has been appended
2020-11-05 15:00:37.878  WARN   --- [           main] i.m.p.j.t.InliningClassTransformer       : Failed to transform class java/lang/Object

java.lang.IllegalArgumentException: Unsupported class file major version 59
at net.bytebuddy.jar.asm.ClassReader.<init>(ClassReader.java:195)
at net.bytebuddy.jar.asm.ClassReader.<init>(ClassReader.java:176)
at net.bytebuddy.jar.asm.ClassReader.<init>(ClassReader.java:162)
at net.bytebuddy.utility.OpenedClassReader.of(OpenedClassReader.java:86)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:3394)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1933)
at net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.make(RedefinitionDynamicTypeBuilder.java:217)
at net.bytebuddy.dynamic.scaffold.inline.AbstractInliningDynamicTypeBuilder.make(AbstractInliningDynamicTypeBuilder.java:120)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3404)
at io.mockk.proxy.jvm.transformation.InliningClassTransformer.transform(InliningClassTransformer.kt:77)
at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:246)
at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:563)
at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:167)
at io.mockk.proxy.jvm.transformation.JvmInlineInstrumentation.retransform(JvmInlineInstrumentation.kt:28)
at io.mockk.proxy.common.transformation.RetransformInlineInstrumnetation$execute$1.invoke(RetransformInlineInstrumnetation.kt:19)
at io.mockk.proxy.common.transformation.RetransformInlineInstrumnetation$execute$1.invoke(RetransformInlineInstrumnetation.kt:6)
at io.mockk.proxy.common.transformation.ClassTransformationSpecMap.applyTransformation(ClassTransformationSpecMap.kt:41)
at io.mockk.proxy.common.transformation.RetransformInlineInstrumnetation.execute(RetransformInlineInstrumnetation.kt:16)
at io.mockk.proxy.jvm.ProxyMaker.inline(ProxyMaker.kt:88)
at io.mockk.proxy.jvm.ProxyMaker.proxy(ProxyMaker.kt:30)

kotlin.UninitializedPropertyAccessException: lateinit property taService has not been initialized

exception. (The same happens if I use tmpService. It doesn't matter if the imported service is in the same or a different package as the test class.)

As you can see from the code of my test class I already played around a lot but without any success so far.

package com.xxx.emr.tm

import com.xxx.data.emr.model.PatientAssignment
import com.xxx.data.tm.TMPService
import com.xxx.data.tm.model.MyType
import com.xxx.data.tm.model.MyTypeCode
import com.xxx.data.tm.model.UserAction
import com.xxx.emr.service.model.AuthenticatedUser
import io.mockk.every
import io.mockk.impl.annotations.InjectMockKs
import io.mockk.impl.annotations.SpyK
import io.mockk.junit5.MockKExtension
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.verify
import org.junit.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest

 @ExtendWith(SpringExtension::class, MockKExtension::class)
 @SpringBootTest // (classes = [com.xxx.emr.service.MyServiceSpringBoot::class])
// @RunWith(SpringJUnit4ClassRunner::class)    
// @ActiveProfiles("test")
// @EnableAutoConfiguration
// @AutoConfigureMockMvc
class MyTest {
// (
//    val tmpService: TMPService,
//    val taService: TaService
// )

//    @InjectMockKs
//    @Autowired
//    @SpyK
@MockK
private lateinit var tmpService: TMPService

@InjectMockKs
@Autowired
private lateinit var taService: TaService

@Test
fun assignAToB() {
    
    MockKAnnotations.init(this, relaxUnitFun = true)

    val vId = "xxx"
    val authUser: AuthenticatedUser = AuthenticatedUser.mockedUser()
    val userAction = UserAction(
        userId = "user_id",
        actionCode = ActionType.GET_IT,
        sessionId = "sessionId"
    )

    val xxx = MyType(
        MyTypeCode.ABC.value,
        MyTypeCode.ABC.name,
    )

    val actionResult = UserActionResult(hashMapOf("success" to true))

//        every { tmpService.getXxx(any()) } returns xxx

    verify( exactly = 1 ) {
        tmpService.doSomething(any(), any(), any())
    }

    verify( exactly = 1 ) {
         taService.dowhatYouWant(„vId", authUser, userAction)
    }
}

Autowiring does not work...so what is my fault? How can I make the test run at all?


Solution

  • I think you should initialize your mocks first, by adding this in your test file for example:

    @org.springframework.test.context.event.annotation.BeforeTestMethod
    fun initMocks() {
        org.mockito.MockitoAnnotations.initMocks(this)
    }
    

    I created a post on another thread about how to setup unit testing, comparable with your requirements: https://stackoverflow.com/a/64669499/7919904