Why doesn't guice requestInjection work on Kotlin object - kotlin

I am new to Guice. I am trying to use requestInjection to inject the dependencies of a kotlin singleton object in this way.
APPROACH 1:
class SampleTest {
#Test
fun test() {
Guice.createInjector(object: KotlinModule() {
override fun configure() {
requestInjection(A)
}
})
assertEquals("Hello world", A.saySomething())
}
}
object A {
#Inject
private lateinit var b: B
fun saySomething(): String {
return b.sayHello()
}
}
class B {
fun sayHello(): String {
return "Hello world"
}
}
But I am getting this error:
kotlin.UninitializedPropertyAccessException: lateinit property b has not been initialized
If I change A to a class with no-arg constructor, it works.
APPROACH 2:
class SampleTest {
#Test
fun test() {
val a = A()
Guice.createInjector(object: KotlinModule() {
override fun configure() {
requestInjection(a)
}
})
assertEquals("Hello world", a.saySomething())
}
}
class A {
#Inject
private lateinit var b: B
fun saySomething(): String {
return b.sayHello()
}
}
class B {
fun sayHello(): String {
return "Hello world"
}
}
Instead, if I change requestInjection to requestStaticInjection, it also works.
APPROACH 3:
class SampleTest {
#Test
fun test() {
Guice.createInjector(object: KotlinModule() {
override fun configure() {
requestStaticInjection<A>()
}
})
assertEquals("Hello world", A.saySomething())
}
}
object A {
#Inject
private lateinit var b: B
fun saySomething(): String {
return b.sayHello()
}
}
class B {
fun sayHello(): String {
return "Hello world"
}
}
Why didn't APPROACH 1 work? Why did APPROACH 2 and APPROACH 3 work?

Kotlin's objects are treated as language static singletons, i.e. their initialization/instantiations happens outside the scope of the dependency injection framework.
Therefor, when using the KotlinModule to inject an object, you have to use requestStaticInjection like in APPROACH 3, or change that object to a class, so that the Guice KotlinModule sees it as non-static, as presented in APPROACH 2
Hope that clarifies things a bit.

Related

Mockk #OverrideMockKs not working with Kotest

I've using Kotest recently and I hadn't had any issues, but recently I was trying some annotations for dependency injection so to simplify the problem I created some basic classes with some methods that just print some messages, just for the sake of learning how to use Kotest and Mockk, but during the testing, I ran with the exception that the variable hasn't been initialized when trying to run the test.
These are my classes
class DefaultClass : AbstractClass() {
private val anotherClass: AnotherClass = AnotherClass()
fun testMethod(value: String): String {
val normalizeValue = value.trim().lowercase().replace(Regex("[^ A-Za-z\\d]*"), "")
return runBlocking {
anotherClass.someOtherMethod()
callsProtectedMethod(normalizeValue)
}
}
private suspend fun callsProtectedMethod(value: String) = coroutineScope {
println("Original method")
returnDefaultString(value)
}
}
AnotherClass
class AnotherClass {
fun someOtherMethod(): Unit {
println("SomeOtherMethod original")
}
}
Test
class DefaultClassTest : FunSpec({
context("Testing DefaultClass") {
#MockK
lateinit var anotherClass: AnotherClass
#OverrideMockKs
lateinit var defaultClass: DefaultClass
beforeContainer {
MockKAnnotations.init(this)
}
test("testing mocks") {
defaultClass.testMethod("some method")
}
}
I've changed the initialization to beforeTest, taken it out of the context, and also use beforeContainer, beforeTest, beforeSpec, but none of these work... every time I still get lateinit property defaultClass has not been initialized
So, I recreated the same test using JUnit and I don't have this issue.
class DefaultClassJUnitTest {
companion object {
#MockK
lateinit var anotherClass: AnotherClass
#OverrideMockKs
lateinit var defaultClass: DefaultClass
#BeforeAll
#JvmStatic
fun setup() {
MockKAnnotations.init(this)
}
}
#Test
fun `Testing with JUnit`() {
every { anotherClass.someOtherMethod() } answers {
println("Mocking another class")
}
val value = defaultClass.testMethod("some method")
}
}
So I'm pretty sure that I'm doing something wrong when using Kotest. I hope anyone might help me, thanks...
I think MockK is probably not looking for variables defined within function scopes. If you want to use the annotations, you likely have to move them to the companion object, like this:
class DefaultClassTest : FunSpec({
context("Testing DefaultClass") {
beforeContainer {
MockKAnnotations.init(this)
}
test("testing mocks") {
defaultClass.testMethod("some method")
}
}
}) {
companion object {
#MockK
lateinit var anotherClass: AnotherClass
#OverrideMockKs
lateinit var defaultClass: DefaultClass
}
}

Referring the class that extends this class in a function

https://pl.kotl.in/WJxo0DujU (below is the code in the link)
open class A() { }
class B(): A() {
fun pew2() { }
}
fun <a: A> a.pew() = apply { }
fun main() {
val b = B()
b.pew().pew2()
}
Is there a way to have the function pew() in class A (not in A's companion object) and still be able to type b.pew().pew2() (not b.apply { pew().pew2() })?
You could create the pew() method in A and have the method return the instance itself:
open class A() {
fun pew() = this
}
class B(): A() {
fun pew2() { }
}
fun main() {
val b = (B().pew() as B).pew2()
}
You can override the function in B and narrow the return type.
open class A() {
open fun pew(): A {
//...
return this
}
}
class B: A() {
fun pew2() { }
override fun pew(): B {
super.pew()
return this
}
}
Kotlin doesn't have a self type that would make these kinds of chainable functions easier to implement, but the apply function makes chainable functions that return the same object unnecessary.

Is it possible to verify at compile time whether the required function is called for the Factory Class in Kotlin?

class ModelFactory {
fun setA() : ModelFactory {
// blabla...
}
fun setB() : ModelFactory {
// blabla...
}
fun setC() : ModelFactory {
// blabla...
}
fun build() : Model {
// An error occurs if any of setA, setB, and setC is not called.
}
}
//example
fun successTest() {
ModelFactory().setA().setB().setC().build() // No error occurs at compile time
}
fun failTest() {
ModelFactory().setA().build() // An error occurs at compile time because setB and setC are not called.
}
It's awkward grammatically, but I think it's been expressed what I want.
I have already implemented an error-raising runtime for this requirement, but I want to check this at compile time.
If possible, I think I should use annotations. But is this really possible at compile time?
With Kotlin, I have been avoiding builder pattern, as we can always specify default values for non-mandatory fields.
If you still want to use a builder pattern, you can use Step builder pattern that expects all mandatory fields to be set before creating the object. Note that each setter method returns the reference of next setter interface. You can have multiple Step builders based on the combination of mandatory fields.
class Model(val a: String = "", val b: String = "", val c: String = "")
class StepBuilder {
companion object {
fun builder(): AStep = Steps()
}
interface AStep {
fun setA(a: String): BStep
}
interface BStep {
fun setB(b: String): CStep
}
interface CStep {
fun setC(c: String): BuildStep
}
interface BuildStep {
//fun setOptionalField(x: String): BuildStep
fun build(): Model
}
class Steps : AStep, BStep, CStep, BuildStep {
private lateinit var a: String
private lateinit var b: String
private lateinit var c: String
override fun setA(a: String): BStep {
this.a = a
return this
}
override fun setB(b: String): CStep {
this.b = b
return this
}
override fun setC(c: String): BuildStep {
this.c = c
return this
}
override fun build() = Model(a, b , c)
}
}
fun main() {
// cannot build until you call all three setters
val model = StepBuilder.builder().setA("A").setB("B").setC("C").build()
}

How to get a reference to delegated instance in Kotlin using delegation 'by'?

Is there any way to get a reference to the delegated object in Kotlin?
Here's an example:
interface A {
fun test()
}
class B: A {
override fun test() {
println("test")
}
}
class C: A by B() {
override fun test() {
// ??? how to get a reference to B's test() method?
}
}
There's currently no way to do that directly. You can achieve that by storing it in a property declared in the primary constructor as follows:
class C private constructor(
private val bDelegate: B
) : A by bDelegate {
constructor() : this(B())
/* Use bDelegate */
}
Another workaround to hotkey's answer might be to include a delegate value
on the A interface...
interface A {
val delegate: A
fun test()
}
class B: A {
override val delegate get() = this
override fun test() {
println("test")
}
}
class C: A by B() {
override fun test() {
delegate.test()
}
}
... this is useful for when zero-arg constructors are required by a framework, e.g. Android Activities

Replacing SAM-constructor with lambda with covariant type

I have got the following Java interfaces:
interface Action1<T> {
void call(T t);
}
interface Test<T> {
void test(Action1<? super T> action)
}
And the following Kotlin class:
interface A {
fun go()
}
abstract class Main {
abstract fun a(): Test<out A>
fun main() {
a().test(Action1 { it.go() })
a().test { it.go() }
}
}
Now in the function main, the first statement compiles, but IntelliJ gives a warning that the SAM-constructor can be replaced with a lambda.
This would result in the second statement.
However, this second statement does not compile, because it has type Any?, not A. Removing the out modifier makes it compile again.
Why does this happen?
The use case of this is when the implementing class of Main needs to return Test<B> for the function a(), where B implements A:
class B : A {
override fun go() {
TODO()
}
}
class MainImp : Main() {
override fun a(): Test<out A> {
val value: Test<B> = object : Test<B> {
override fun test(action: Action1<in B>?) {
TODO()
}
};
return value
}
}
It is a compiler bug. You can track it here: https://youtrack.jetbrains.com/issue/KT-12238.