MockK: mocking an jpa repository.save call - kotlin

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

Related

How to read properties as immutable strings in Kotlin

I have been trying to read the read the properties in the Kotlin code. The lateinit var gets the work done but since it is mutable, the value can be changed even after initialisation. I want to read a property from a file and to not worry about it being changed anywhere in the file. I want something like lateinit val which is not present in Kotlin; or you somehow able to add #Value inside by lazy block of code.
I am working with AWS Secret Manager so I am putting the same code here but my doubt is more generic and not specific to AWS.
#Value("\${aws.secretsManager.region}")
private lateinit var region: String
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(Region.of(region))
.build()
}
I tried doing the similar thing with by lazy:
#Value("\${aws.secretsManager.region}")
private lateinit var region: String
private val awsRegion: Region by lazy {
Region.of(region)
}
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(awsRegion)
.build()
}
The above codes are working fine but it would be much cleaner if there's a way to merge these 2 lines:
#Value("\${aws.secretsManager.region}")
private lateinit var region: String
private val awsRegion: Region by lazy {
Region.of(region)
}
In your specific case you can inject property directly into bean method as an argument (method arguments are immutable)
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(
#Value("\${aws.secretsManager.region}") region: String
): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(Region.of(region))
.build()
}
or if you need this property in multiple #Beans you can inject it into constructor of enclosing configuration class
#Confiuration
class SomeConfig(
#Value("\${aws.secretsManager.region}")
private val region: String
) {
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(Region.of(region))
.build()
}
}

Possible to use Spring AOP (AspectJ) with Kotlin properties?

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

Is there a cleaner way to set a top-level variable later in code without making it a lateinit var?

So what I want to achieve is that to have the top-level variable set some time later in the main function, but I don't want to make it a lateinit var which certainly breaks the Extension variable functionality.
For instance this code doesn't work since extension variables don't support lateinit modifier:
lateinit var Dispatchers.Konvironment: MainCoroutineDispatcher
private set
fun main() {
...
Dispatchers.Konvironment = ArbitraryMainDispatcher(Thread.currentThread()) { queue.add(it) }
...
}
So what I finally came up with is to use a dummy variable and implement the getter of the val variable.
val Dispatchers.Konvironment: MainCoroutineDispatcher
get() = dispatcher
private lateinit var dispatcher: MainCoroutineDispatcher
fun main() {
...
dispatcher = ArbitraryMainDispatcher(Thread.currentThread()) { queue.add(it) }
...
}
But it is certainly not clean way to do that. It looks ugly (ish) creating multiple variable in the top-level structure is not very clean architecture.
So is there any possible clean workarounds? Sort of like lazy initialization, by some delegates or something.
Well, partially answering your question:
var Dispatchers.Konvironment: MainCoroutineDispatcher
get() = dispatcher
private set(value) {
dispatcher = value
}
private lateinit var dispatcher: MainCoroutineDispatcher
fun main() {
...
Dispatchers.Konvironment = ArbitraryMainDispatcher(Thread.currentThread()) { queue.add(it) }
...
}
will give you the desired way of assigning the value. There is no way to get rid of this additional lazyinit variable, though.
Extensions are nothing more than just some Kotlin syntax sugar for static methods which take an instance of the extended class as one of the arguments, and perform some action. If you're familiar with Java then, for example, these extensions:
// Extensions.kt
fun Foo.extendedAction() {
println(this)
}
var Foo.extendedBar: Bar
get() = this.bar
set(value) {
this.bar = value
}
are under the hood these methods in Java:
public class ExtensionsKt {
public static final void extendedAction(Foo foo) {
System.out.println(foo);
}
public static final Bar getExtendedBar(Foo foo) {
return foo.getBar();
}
public static final Bar setExtendedBar(Foo foo, Bar bar) {
foo.setBar(bar);
}
}
The conclusion which maybe drawn from the above is that extensions don't actually add anything to the extended classes' signatures, they simply decorate them with additional functionality. Or, as put in the docs:
Extensions do not actually modify classes they extend. By defining an extension, you do not insert new members into a class, but merely make new functions callable with the dot-notation on variables of this type.
So you can see, unless dispatcher somehow already exists within Dispatchers, you can't do what you want without providing an external, "backing" variable which value can be actually referenced by the extension.

Mock private property with mockk throws an excpetion

I'm using mockk for my testing in kotlin. But I can't seem to override a private property in a spy object.
I have this object
private val driverMapSnapshotMap: MutableMap<Int, SnapshotImage> = mutableMapOf()
in a class that I spy on using
viewModel = spyk(DriverListViewModel(), recordPrivateCalls = true)
But when I try to make it fill up with mock values I get an error
every {
viewModel getProperty "driverMapSnapshotMap"
} returns(mapOf(1 to mockkClass(SnapshotImage::class)))
The error I get
io.mockk.MockKException: Missing calls inside every { ... } block.
Any thoughts?
Here is a solution to access private fields in Mockk for classes( for objects it is even simpler )
class SaySomething {
private val prefix by lazy { "Here is what I have to say: "}
fun say( phrase : String ) : String {
return prefix+phrase;
}
}
#Before
fun setUp() = MockKAnnotations.init(this, relaxUnitFun = true)
#Test
fun SaySomething_test() {
mockkConstructor(SaySomething::class)
every { anyConstructed<SaySomething>() getProperty "prefix" } propertyType String::class returns "I don't want to say anything, but still: "
val ss = SaySomething()
assertThat( ss.say("Life is short, make most of it"), containsString( "I don't want to say anything"))
}
It is nearly impossible to mock private properties as they don't have getter methods attached. This is kind of Kotlin optimization and solution is major change.
Here is issue opened for that with the same problem:
https://github.com/mockk/mockk/issues/263
It should be
every {
viewModel getProperty "driverMapSnapshotMap"
} returns mock(DriverRemoteModel::class)

How to use MicronautTest with Kotlintest to inject beans while testing ? in Kotlin

How to inject the following into Test, as no constructor args are allowed
and its failed to initialise the injected beans
#MicronautTest
class ApplicationTest:StringSpec() {
#Inject
lateinit val embeddedServer:EmbeddedServer;
#Inject
lateinit val dataSource:DataSource
init{
"test something"{
//arrange act assert
}
}
}
You need to specify Project config by creating an object that is derived from AbstractProjectConfig, name this object ProjectConfig and place it in a package called io.kotlintest.provided. KotlinTest will detect it's presence and use any configuration defined there when executing tests.
as per the documentation
https://github.com/kotlintest/kotlintest/blob/master/doc/reference.md#project-config
object ProjectConfig :AbstractProjectConfig() {
override fun listeners() = listOf(MicornautKotlinTestExtension)
override fun extensions() = listOf(MicornautKotlinTestExtension)
}
Because the test cases are passed like a lambda to the parent class constructor, you have to use constructor injection
#MicronautTest
class ApplicationTest(
private val embeddedServer: EmbeddedServer,
private val dataSource: DataSource
): StringSpec({
"test something"{
//arrange act assert
}
})
You can look at any of the tests in the project for a running example. https://github.com/micronaut-projects/micronaut-test/blob/master/test-kotlintest/src/test/kotlin
Have you tried to write your code like this ?
#MicronautTest
class ApplicationTest:StringSpec() {
val embeddedServer:EmbeddedServer
val dataSource:DataSource
#Inject
ApplicationTest(embeddedServer:EmbeddedServer, dataSource:DataSource) {
this.embeddedServer = embeddedServer
this.dataSource = dataSource
}
init{
"test something"{
//arrange act assert
}
}
}
This should work.