I have class which has two methods, and I am trying to test these methods. class exampleClass(private val exampleRepository: ExampleRepository) the class has repository as parameter, and when I try to add lateinit var exampleRepository: ConfigRepository in the test class I am getting error lateinit property exampleRepository has not been initialized. How can I initialized the repository?
Related
I am doing this way in abstract class
#Autowired
lateinit var fileContract: FileContract
with error
kotlin.UninitializedPropertyAccessException: lateinit property fileContract has not been initialized
But the same works in regular class. Why?
Have you tried adding #Component annotation to the abstract class that had the uninitialized field? We run into a similar problem, that sorted it for us.
You should use constructor injection and not field injection where possible. That also would solve your problem, because you do not need to autowire anything in your abstract class, but you just declare it as a constructor parameter:
abstract class AbstractExtractor(
val fileContract: FileContract,
val dictionaryContractImpl: DictionaryContractImpl,
val regulationContractImpl: RegulationContractImpl
) {
...
}
Note that the above notation declares fileContract, dictionaryContractImpl and regulationContractImpl as constructor parameters, and at the same time (due to the val keyword) as a local property of the class AbstractExtractor. This means that it is not necessary to declare any additional variables for them inside the class.
Now, your subclass RegulationExtractor also needs to use constructor injection, so that it can pass the autowired values on to the constructor of the super class:
#Service
class RegulationExtractor(
fileContract: FileContract,
dictionaryContractImpl: DictionaryContractImpl,
regulationContractImpl: RegulationContractImpl
) : AbstractExtractor(
fileContract,
dictionaryContractImpl,
regulationContractImpl
) {
...
}
If you need any of the constructor parameters also in the RegulationExtractor class, you can add the val keyword like in AbstractExtractor.
It should not be necessary to add the #Autowired annotation here, but if you want, you can change the above code to
#Service
class RegulationExtractor #Autowired constructor(
...
I am having trouble using my application.properties values while testing.
Here's my code:
#Repository
class RedisSubscriptionStore(): SubscriptionStore {
#Value("\${default.package}")
lateinit var defaultPackageForFCMInstance: String }
and this works as expected. It's used in my updateSubscription method with the correct value from application.properties
The problem is in my test when I use the class the value is not instantiated and throws an error
#SpringBootTest()
#Tag("integration")
internal class RedisSubscriptionStoreIntegTest #Autowired constructor(
val objectMapper: ObjectMapper,
val redisTemplate: RedisTemplate<String, String>
) {
val sut = RedisSubscriptionStore(redisTemplate, objectMapper)
#Test
fun `return 200 when updating subscription`() {
sut.updateSubscription(updatedSub)
//Assert
val newlyUpdatedSub = getSubscription(updatedSub.peerID)
Assertions.assertEquals(updatedSub, newlyUpdatedSub)
}
but this throws the error: lateinit property defaultPackageForFCMInstance has not been initialized
even though this works with the correct value:
#SpringBootTest()
#Tag("integration")
internal class RedisSubscriptionStoreIntegTest #Autowired constructor(
val objectMapper: ObjectMapper,
val redisTemplate: RedisTemplate<String, String>
) {
#Value("\${default.package}")
lateinit var defaultPackageForFCMInstance: String
}
so why is my defaultPackageForFCMInstance not initialized when calling from my test class? It obviously have a value, I've tried printing it out in the test class before calling it from sut
Your are instantiating the RedisSubscriptionStore
So there is no way for Spring to inject value. Either also pass the value in the constructor or use injecation in the test
#Autowired
var sut: RedisSubscriptionStore
Spring Boot allows to inject a list of all implementations of an interface (SomeComponent) as List into another component (SomeOtherComponent), e.g.
#Component
interface SomeComponent
#Component
class SomeComponentImpl0 : SomeComponent
#Component
class SomeComponentImpl1 : SomeComponent
class SomeOtherComponent {
#Autowired
lateinit var impls: List<SomeComponent>
}
How can I inject mocks for the implementations using MockK annotations? In
import io.mockk.MockKAnnotations
import io.mockk.impl.annotations.InjectMockKs
import io.mockk.impl.annotations.MockK
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
class SomeOtherComponentTest {
#MockK
lateinit var someComponentImpl0: SomeComponentImpl0
#MockK
lateinit var someComponentImpl1: SomeComponentImpl1
#InjectMockKs
lateinit var instance: SomeOtherComponent
#BeforeEach
fun setup() {
MockKAnnotations.init(this)
}
#Test
fun testSomething() {
println(instance.impls.toString())
}
}
I'm getting either
io.mockk.MockKException:
No matching constructors found:
constructor(impls : kotlin.collections.List<de.richtercloud.inject.mocks.foor.list.of.impl.SomeComponent> = <not able to lookup>)
at de.richtercloud.inject.mocks.foor.list.of.impl.SomeOtherComponentTest.setup(SomeOtherComponentTest.kt:40)
if I'm using constructor injecction and
kotlin.UninitializedPropertyAccessException: lateinit property impls has not been initialized
at de.richtercloud.inject.mocks.foor.list.of.impl.SomeOtherComponentTest.testSomething(SomeOtherComponentTest.kt:26)
if I'm using an #Autowired var property in the class.
I'm using 1.3.50 through Maven 3.6 and MockK 1.9.3.
Add #ExtendWith(MockKExtension::class) above your Test class to make #InjectMokKs work.
I had the same problem here. It worked when I removed lateinit from the component being tested, and instantiated it on declaration.
For example, changed:
#InjectMockKs
lateinit var instance: SomeOtherComponent
to:
#InjectMockKs
var instance = SomeOtherComponent()
I have a class:
open class AbstractMapper<E : AbstractEntity, D : AbstractDto> #Autowired constructor(
protected val mapper: ModelMapper
) : EntityDtoMapper<E, D>
It have autowired bean ModelMapper in main constructor. I try to inherit other class from it:
class UserParamsMapper : AbstractMapper<UserParams, UserParamsDto>()
IDE ask to declare field, autowired in class-paernt:
No value passed for parameter ModelMapper
Please advice, how to do it? Or I can autowire bean in AbstractMapper other way?
You need to pass all superclass constructor arguments in the subclass constructor. The #Autowired annotation is pointless on an abstract class constructor, as it only applies to the constructor of a class that is instantiated itself. You can make this work by changing your subclass:
class UserParamsMapper #Autowired constructor(
mapper: ModelMapper
) : AbstractMapper<UserParams, UserParamsDto>(mapper)
Alternatively you can change to field injection instead of constructor injection in your superclass.
open class AbstractMapper<E : AbstractEntity, D : AbstractDto> : EntityDtoMapper<E, D> {
#field:Autowired
protected lateinit var mapper: ModelMapper
}
Is it possible to do something like following in Kotlin?
#Autowired
internal var mongoTemplate: MongoTemplate
#Autowired
internal var solrClient: SolrClient
Recommended approach to do Dependency Injection in Spring is constructor injection:
#Component
class YourBean(
private val mongoTemplate: MongoTemplate,
private val solrClient: SolrClient
) {
// code
}
Prior to Spring 4.3 constructor should be explicitly annotated with Autowired:
#Component
class YourBean #Autowired constructor(
private val mongoTemplate: MongoTemplate,
private val solrClient: SolrClient
) {
// code
}
In rare cases, you might like to use field injection, and you can do it with the help of lateinit:
#Component
class YourBean {
#Autowired
private lateinit var mongoTemplate: MongoTemplate
#Autowired
private lateinit var solrClient: SolrClient
}
Constructor injection checks all dependencies at bean creation time and all injected fields is val, at other hand lateinit injected fields can be only var, and have little runtime overhead. And to test class with constructor, you don't need reflection.
Links:
Documentation on lateinit
Documentation on constructors
Developing Spring Boot applications with Kotlin
Yes, java annotations are supported in Kotlin mostly as in Java.
One gotcha is annotations on the primary constructor requires the explicit 'constructor' keyword:
From https://kotlinlang.org/docs/reference/annotations.html
If you need to annotate the primary constructor of a class, you need to add the constructor keyword to the constructor declaration, and add the annotations before it:
class Foo #Inject constructor(dependency: MyDependency) {
// ...
}
You can also autowire dependencies through the constructor. Remember to annotate your dependencies with #Configuration, #Component, #Service etc
import org.springframework.stereotype.Component
#Component
class Foo (private val dependency: MyDependency) {
//...
}
like that
#Component class Girl( #Autowired var outfit: Outfit)
If you want property injection but don't like lateinit var, here is my solution using property delegate:
private lateinit var ctx: ApplicationContext
#Component
private class CtxVarConfigurer : ApplicationContextAware {
override fun setApplicationContext(context: ApplicationContext) {
ctx = context
}
}
inline fun <reified T : Any> autowired(name: String? = null) = Autowired(T::class.java, name)
class Autowired<T : Any>(private val javaType: Class<T>, private val name: String?) {
private val value by lazy {
if (name == null) {
ctx.getBean(javaType)
} else {
ctx.getBean(name, javaType)
}
}
operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value
}
Then you can use the much better by delegate syntax:
#Service
class MyService {
private val serviceToBeInjected: ServiceA by autowired()
private val ambiguousBean: AmbiguousService by autowired("qualifier")
}