Skip to content

@MockitoBean is not properly initialized in Kotlin test class with Spring Boot 3.4.4 #34832

Closed as not planned
@JayAndromeda

Description

@JayAndromeda

I am running into an issue when upgrading from Spring Boot 3.3.9 to 3.4.4 where my tests with Mocked beans fail to run due to an initialization error on our lateinit fields. My Mockito versions are managed by the spring-boot-starter-test artifact which would be 5.11.0 -> 5.14.2.

My exact Mockito error is:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Misplaced or misused argument matcher detected here: <package path>FooTest.anyTokenPayLoad.kt 

I think this is caused by a Kotlin exception:

kotlin.UninitializedPropertyAccessException: lateinit property tokenType has not been initialized

I looked at this issue #34516 which is strikingly similar to our issue but of course we do not use spy beans.

These tests work as intended on Spring Boot 3.3.9 and Mockito 5.11.0. They do not work on Spring Boot 3.4.4 and do not work on either Mockito version which leads me to believe it is a possible issue with how spring is initializing these lateinit fields via @Autowired.

Thank you for taking a look into and I apologize if it was answered somewhere but I tried to look in the issues list.

Other info:

  • 'kotlin_version', "1.7"
  • 'org.jetbrains.kotlin.plugin.spring' version '2.1.20'
  • 'org.jetbrains.kotlin.jvm' version '2.1.20'

I attached stubbed versions of our test class, service class that allows the autowire, and object that holds the lateinit field in question.

import org.mockito.Mockito.`when`
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.test.context.SpringBootTest
import org.junit.jupiter.api.Test

@ExtendWith(SpringExtension::class)
@EnableConfigurationProperties(value = [TokenConfiguration::class])
@ContextConfiguration(classes = [TokenService::class])
@SpringBootTest    
open class FooTest {
    
    @Autowired
    private lateinit var subject : TokenService

    @Autowired
    private lateinit var tokenConfiguration : TokenConfiguration

    @MockitoBean
    private lateinit var tokenGenerator : TokenGenerator
    
    @Test
    fun `generateToken with valid generate token request returns access token`() {
        val generateTokenRequest = GenerateTokenRequest(foo, bar)
        `when`(tokenGenerator.generateAccessToken(anyTokenPayLoad(generateTokenRequest))).thenReturn(accessToken)
        /* 
        We also tried a stub such as the following line but it gave us the same error

        `when`(tokenGenerator.generateAccessToken(any(TokenPayLoad::class.java))).thenReturn(accessToken) */
        val response = subject.generateToken(generateTokenRequest)
        assertThat(response).isNotNull
        assertThat(response.ttl).isEqualTo(ttl)
        assertThat(response.tokenType).isEqualTo(tokenType)
    }

    @Suppress("UNCHECKED_CAST")
    private fun <TokenPayLoad> anyTokenPayLoad(generateTokenRequest: GenerateTokenRequest): TokenPayLoad {
        return any() ?: TokenPayLoad(generateTokenRequest, tokenConfiguration) as TokenPayLoad
    }
}
import org.springframework.beans.factory.annotation.Value

@ConfigurationProperties
@RefreshScope
open class TokenConfiguration {

    @Value("Bearer")
    lateinit var tokenType: String

    @Value("3600L")
    var defaultTTL: Long = 0L
}

@Service
@EnableConfigurationProperties(value = [TokenConfiguration::class])
class TokenService
@Autowired
constructor(private var tokenGenerator: TokenGenerator,
            private var tokenConfiguration: TokenConfiguration) {
    fn generateToken(): GenerateTokenResponse{
        //some logic
        return generateTokenResponse 
    }             
} 

Metadata

Metadata

Assignees

No one assigned

    Labels

    in: testIssues in the test module

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions