i'm quite new to Kotlin and having troubles understanding my issue with initalizing a vertx EventBus.
I'm using Kotlin with Quarkus (which might not be to relevant here) and Vertx in a DDD context.
I have a model, which provides a static method for sending an update. This static method creates the model and the model tries to inject the EventBus:
package model
import java.util.*
import javax.enterprise.context.ApplicationScoped
#ApplicationScoped
class EventSet(
) {
var events = listOf<Event>()
#field:Default
#Inject
lateinit var bus: EventBus
// kotlin.UninitializedPropertyAccessException: lateinit property bus has not been initialized
fun sendAndStore() {
... persist() happens here and other things
bus.publish(UPDATE, entity)
}
companion object {
const val UPDATE = "update"
fun create(events: List<Event>): EventSet {
if (events.isEmpty()) {
throw Exception()
}
val eventSet = EventSet()
eventSet.events = events
return eventSet
}
}
}
When i'm running my unit test (Annotated with #QuarkusTest on the class, #Test and #TestTransaction on the function, i'm getting kotlin.UninitializedPropertyAccessException: lateinit property bus has not been initialized
I don't understand it at all. My assumption was that i can inject it whenever i want. Unfortunate the constructor injection would require me to loop through it through the companion object as i was also not able to inject anything into my static method (which i assume shouldn't work anyway).
How do i debug this type of issue? I checked the quarkus dev console and the Vertx/EventBus is marked as a #Singleton bean, not sure if that makes any difference.
Thanks,
Sigi
Related
Is it possible to use Spring AOP (AspectJ) with Kotlin properties? Specifically due to how Kotlin compiles properties to Java:
a getter method, with the name calculated by prepending the get prefix
a setter method, with the name calculated by prepending the set prefix (only for var properties)
a private field, with the same name as the property name (only for properties with backing fields)
Consider the following minimal reproducible example:
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
annotation class TestAnnotation
...
#Aspect
class TestAspect {
#Around("#annotation(annotation)")
fun throwingAround(joinPoint: ProceedingJoinPoint, annotation: TestAnnotation): Any? {
throw RuntimeException()
}
}
...
internal class MinimalReproducibleExample {
open class TestProperties {
#TestAnnotation
val sampleProperty: String = "sample property"
#TestAnnotation
fun sampleFunction(): String = "sample function"
}
private lateinit var testProperties: TestProperties
#BeforeEach
fun setUp() {
val aspectJProxyFactory = AspectJProxyFactory(TestProperties())
aspectJProxyFactory.addAspect(TestAspect())
val aopProxyFactory = DefaultAopProxyFactory()
val aopProxy = aopProxyFactory.createAopProxy(aspectJProxyFactory)
testProperties = aopProxy.proxy as TestProperties
}
#Test
fun test() {
println(testProperties.sampleProperty)
println(testProperties.sampleFunction())
}
}
Running the test yields:
null
sample function
When debugging I can see that the generated proxy is a cglib-backed proxy, which should be able to proxy to a concrete class, but it does not seem to invoke the configured aspect. Is there something wrong with my #Around definition, or is this a limitation of Kotlin properties and/or proxying concrete classes?
Was able to trigger the aspect above with the following changes:
Use a "site target" for the getter: #get:TestAnnotation
Make the property/function both open
I am using context() in my GlanceAppWidget() for tasks like retrieving glanceId and updating app widget state. I am having issue with how I inject the context object.
I would like to use the dagger/hilt framework to inject the context into my GlanceAppWidget() constructor. See MyWidget() below.
However by injecting the context into MyWidget, I then need to pass the context as constructor parameter in MyWidgetReceiver() for val glanceAppWidget. Broadcast receivers are not meant to have constructor arguments so this gives me an Instantiation Exception.
How can I inject context into my GlanceAppWidget? Any help will be much appreciated.
Note: I have also tried using default arguments in MyWidget() to avoid providing context in MyWidgetReceiver but this throws "Type may only contain one injected constructor".
#Singleton
class MyWidget #Inject constructor(
#ApplicationContext val context: Context
) : GlanceAppWidget()
#AndroidEntryPoint
#Singleton
class MyWidgetReceiver #Inject constructor(
#ApplicationContext val context: Context /*<-java.lang.InstantiationException when trying to inject into BroadcastReceiver*/
) : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget
get() = MyWidget(context)
}
onReceive method of the BroadcastReceiver has the context as its argument. You probably want to bind your widget-creating logic to this method
fun onReceive(context: Context, intent: Intent)
EDIT:
I did not noticed that you are using Glance. Since that, I recommend to stop using context in constructor and instead update glanceId and widget state when you will actually have access to context via some kind of method.
override fun onReceive(context: Context, intent: Intent) {
glanceAppWidget.update(context)
}
MyWidget:
fun update(context: Context) {
// do some work
}
In case when you will need update, you will simply send matching intent which will be received by the receiver.
Couple of things:
GlanceAppWidgetReceiver is a BroadcastReceiver thus you can't have constructor parameters. Also it shouldn't be a singleton. BroadcastReceivers are short-term living classes.
You can retrieve the context inside a #Composable function by calling LocalContext.current. Also you can retrieve the glanceId by calling LocalGlanceId.current
Thus you don't need to inject the context in the first place.
class MyWidget: GlanceAppWidget() {
#Composable
override fun Content() {
val context = LocalContext.current
val glanceId = LocalGlanceId.current
//...
}
}
#AndroidEntryPoint
class MyWidgetReceiver: GlanceAppWidgetReceiver() {
override val glanceAppWidget = MyWidget()
}
My code saves an object to database in some bigger method, but I don't need to test this.
So I want to mock the Repository.save method. But the save method returns the saved object.
I tried the following:
#MockK
private lateinit var mockJobRepository: JobRepository
val jobSlot = slot<Job>()
// ...
every { mockJobRepository.save<Job>(capture(jobSlot)) }
returns(jobSlot.captured)
But it throws an runtime error:
"lateinit property captured has not been initialized"
How do I just return the given argument in the mock?
Have you tried
private val mockJobRepository = mockk<JobRepository>()
?
I've notice #Mockk annotations on lateinit vars can be finicky
When using annotations, you have to tell Mockk at some point to initialize the annotated properties. Assuming you're using JUnit 5, you can do it by initializing mocks in #BeforeEach:
class Test {
#MockK
private lateinit var emailService: EmailService
#BeforeEach
fun setUp() {
MockKAnnotations.init(this)
}
}
...or just use the Mockk-Extension for JUnit:
#ExtendWith(MockKExtension::class)
class Test {
#MockK
private lateinit var emailService: EmailService
}
Btw. less verbose option than capturing the argument would be returnsArgument:
every { mockJobRepository.save<Job>(any()) } returnsArgument 0
I have a simple named service which uses a queue:
#Named
class OrderFormService #Inject constructor(
private val repository: OrderFormRepository
) {
private val queue: Queue<OrderForm> = LinkedList()
private val logger: Logger = LoggerFactory.getLogger("service")
fun getNextOrderForm(input: GetNextOrderFormInput): GetNextOrderFormPayload? {
if (queue.isEmpty()) {
logger.info("queue is empty")
val forms: List<OrderForm> = repository.findTop1000ByImageTypeAndImageState(input.type, input.state)
forms.forEach {
queue.offer(it)
}
}
if (!queue.isEmpty()) {
return GetNextOrderFormPayload(queue.poll())
}
return null
}
}
While trying to unit test this I want to mock the queue:
#ExtendWith(MockitoExtension::class)
internal class OrderFormServiceTest {
#Mock
private val queue: Queue<OrderForm> = LinkedList()
#Mock
lateinit var repository: OrderFormRepository
#InjectMocks
lateinit var service: OrderFormService
#Test
fun givenValidInputAndFilledQueueWhenGetNextOrderFormThenReturnPayload() {
// given
val expected = createOrderForm()
val expectedPayload = GetNextOrderFormPayload(expected)
given(queue.isEmpty()).willReturn(false)
given(queue.poll()).willReturn(expected)
// when
val input = GetNextOrderFormInput(ImageType.NUMBER, ImageState.UNCLASSIFIED)
val result = service.getNextOrderForm(input)
// then
assertThat(result).isEqualTo(expectedPayload)
}
}
But the queue is always empty. So I guess the queue is not getting mocked correctly. What am I doing wrong?
EDIT
Things I have tried:
Making queue not final:
...
var queue: Queue<OrderForm> = LinkedList()
...
Using Mockito.mock:
...
var queue = Mockito.mock(Queue::class.java)
`when`(queue.isEmpty()).thenReturn(false)
`when`(queue.poll()).thenReturn(expected)
...
Your queue is not marked as #Autowired or part of the constructor, thus Mockito cannot mock it.
In order to make this work (haven't verified it though), define your constructor like this:
#Named
class OrderFormService #Inject constructor(
private val repository: OrderFormRepository,
private val queue: Queue<OrderForm>
) { }
Now, in order to have the queue initialized in your regular program, you have to define a bean for it, something like:
#Configuration
class QueueConfiguration {
#Bean
fun queue() : Queue<OrderForm> = LinkedList()
}
Furthermore, you should keep in concern that #InjectMocks only will use one injection method. So you can't mix constructor initialisation with property setters or field injection.
Also have a look at #MockBean. This replaces the bean globally and can be more convenient to use. It has the drawback that it dirties the context, resulting in context reinitialization and potentially slower tests if they aren't sliced properly.
EDIT:
Another alternative would be to set the mock manually (samples not verified, hope they work work you). I recommend using https://github.com/nhaarman/mockito-kotlin to make the Mockito syntax more kotlinish.
Setting the mock manually requires to make the queue a publicly settable property:
#Named
class OrderFormService #Inject constructor(
private val repository: OrderFormRepository
) {
var queue: Queue<OrderForm> = LinkedList()
}
Then, you assign the mock in your test:
internal class OrderFormServiceTest {
private val queue: Queue<OrderForm> = mock {}
#Mock
lateinit var repository: OrderFormRepository
#InjectMocks
lateinit var service: OrderFormService
#BeforeEach
fun setup() {
service.queue = queue
}
}
There's one issue with this though: depending on the framework you use, your OrderFormService might be initialised only once. When setting the queue, you change the global object which might affect other tests. To mitigate this, #DirtiesContext on your test will ensure that the whole context is rebuilt (which impacts test performance). This is more or less the same which #MockBean would do for you (with the same performance impact). You can also clean the object yourself though.
The sunflower example app by Google uses a private Class with a companion object to implement a Singleton pattern of their repository instead of simply implementing the repository as an (inherently Singleton) Object.
This is the first time I've seen a Singleton implemented this way in Kotlin instead of implementing it as an Object. In what context(s) should this private constructor implementation be used instead of the more common Object implementation?
class GardenPlantingRepository private constructor(
private val gardenPlantingDao: GardenPlantingDao
) {
suspend fun createGardenPlanting(plantId: String) {
withContext(IO) {
val gardenPlanting = GardenPlanting(plantId)
gardenPlantingDao.insertGardenPlanting(gardenPlanting)
}
}
suspend fun removeGardenPlanting(gardenPlanting: GardenPlanting) {
withContext(IO) {
gardenPlantingDao.deleteGardenPlanting(gardenPlanting)
}
}
fun getGardenPlantingForPlant(plantId: String) =
gardenPlantingDao.getGardenPlantingForPlant(plantId)
fun getGardenPlantings() = gardenPlantingDao.getGardenPlantings()
fun getPlantAndGardenPlantings() = gardenPlantingDao.getPlantAndGardenPlantings()
companion object {
// For Singleton instantiation
#Volatile private var instance: GardenPlantingRepository? = null
fun getInstance(gardenPlantingDao: GardenPlantingDao) =
instance ?: synchronized(this) {
instance ?: GardenPlantingRepository(gardenPlantingDao).also { instance = it }
}
}
}
Using an object is an issue if your singleton instance needs parameters, like in this case here, with GardenPlantingDao, as they cannot take constructor arguments. This comes up frequently on Android, as there's many cases where singletons requires a Context to operate.
You could still use an object in these cases, but it would either be unsafe or inconvenient:
The first option would be to provide it with its dependencies using a setter method before using any of its other methods. This would mean that every other method would have to check if the dependencies have been initialized, and probably throw exceptions in case they haven't, which leads to runtime problems.
Alternatively, you could require any dependencies as arguments to each method of the singleton, which is tedious at the call site.
Hence the "traditional" way of implementing a singleton with a private constructor and a factory method instead.