Description
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
}
}