How do I test generators for delegated properties? - testing

In my current project there is a class that will later on be implemented by many others. This class provides some generators for delegated properties.
abstract class BaseClass {
protected val delegated1 get() = new Delegated1Impl()
protected val delegated2 get() = new Delegated2Impl()
...
}
This base class can be used this way:
class Example : BaseClass() {
var field1 by delegated1
var field2 by delegated2
}
Now I want to test these delegated generators. Some of them contain logic which I want to test, but for now I only want to know that everytime they are called they return a new instance.
Now my question is: how can I test these generators?
The generators are not visible outside of extending classes so I cannot simply create an instance of it and call these methods.
#Test
fun `delegated1 should always return a new instance`() {
val target = object: BaseClass()
val first = target.delegated1 // This does not work since it is protected
val second = target.delegated1
assertTrue(first !== second)
}

You need a new object created whenever you "call" the get method. So how to test it? With a provider
A Provider<T> is just an object that provides you new instances of a concrete class. Its signature is something like this:
interface Provider<T> {
fun get() : T
}
So you need to inject a new Provider<T> into your BaseClass:
abstract class BaseClass(
private val implementation1Provider : Provider<YourInterface>,
private val implementation2Provider : Provider<YourInterface>) {
protected val delegated1 get() = implementation1Provider.get()
protected val delegated2 get() = implementation2Provider.get()
...
}
Now you can inject your custom providers in the test and assert that they have been called:
#Test
fun `delegated1 should always return a new instance`() {
val implementation1Provider = ...
val target = Example(implementation1Provider, ...)
val first = target.field1
// assert that implementation1Provider.get() has been called
}

Related

Kotlin: Hashmap of interface methods by the implementing class name

I have a list of clases that implement a specific interface. The ability to construct those clases or not is not static (so it's not possible to use when(className)), and can be configured so I want to be able to create some clases or call some methods based on a hashMap of allowed "constructors". Then if the key identifying a class is in present in the hashmap I can call the corresponding method, otherwise I can safely ignore. Let me illustrate:
Let's say I have an interface like
interface Instanceable {
data class Config(
val bar: Whatever
)
fun getIntance(config: Config): Instanceable
}
Then I have several (let's say 10) classes that implement this interface
class Implementation1() : Instanceable {
companion object {
const val ID = "INSTANCE_1"
}
private lateinit var foo: Whatever
override fun getIntance(config: Config) = Implementation1().also{ this#Implementation1.foo = config.bar }
}
I want to create a hashmap of the methods by the identifiers, so later down the lane I can grab the method from the hashMap by the key ID and just invoke() the value if it's there. Something like:
allowedInstances("INSTANCE_1")?.let{ it.invoke(someConfig) }
In order to do this I tried to create a hashMap of methods like this:
private val allowedInstances = mutableHashMapOf<String, Instanceable.(Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = Instance1::getIntance
allowedInstances[Instance2.ID] = Instance2::getIntance
allowedInstances[Instance4.ID] = Instance4::getIntance
But it fails with:
Type mismatch.
Required: Instanceable.(Instanceable.Config) → Instanceable
Found: KFunction2<Implementation1, Instanceable.Config, Instanceable>
If I create the hashmap directly and let the compiler infer the types like this:
private val allowedInstances = mutableHashMapOf(
Implementation1.ID to Implementation1::getIntance,
Implementation2.ID to Implementation2::getIntance,
Implementation4.ID to Implementation4::getIntance,
)
Checking the type of the hashmap shows:
HashMap<String, out KFunction2<Nothing, Instanceable.Config, Instanceable>>
In fact I can do:
private val allowedInstances = mutableHashMapOf<String, Nothing.(Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = Instance1::getIntance
allowedInstances[Instance2.ID] = Instance2::getIntance
allowedInstances[Instance4.ID] = Instance4::getIntance
So the actual question is:
Why the function of the second hashMap parameter has Nothing as the receptor? Why I cannot have the interface Instanceable instead?
Edit: Still not good to have the Nothing there:
allowedInstances["INSTANCE_1"]?.let{ it.invoke(Nothing, someConfig) }
//Fails with: Classifier 'Nothing' does not have a companion object, and thus must be initialized here
Edit 2: All of the errors are in compile time
Your function type
Instanceable.(Instanceable.Config) -> Instanceable
is describing an extension function on an instance of Instanceable. You need to omit the receiver from the function type to be able to match your constructors' signature:
(Instanceable.Config) -> Instanceable
Edit: The other half of the problem is that you define getInstance() as a member function of the class. So you have to create an invalid instance of your class to use to create a valid instance, which doesn't make sense.
I would delete the getInstance() function from your interface, and put the equivalent code in the constructor of your class. Then you can define a function type in your Map that constructs your items.
interface Instanceable {
data class Config(
val bar: Whatever
)
// REMOVE this: fun getIntance(config: Config): Instanceable
}
class Implementation1(config: Config) : Instanceable {
companion object {
const val ID = "INSTANCE_1"
}
private val foo: Whatever = config.bar
}
private val allowedInstances = mutableHashMapOf<String, (Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = ::Implementation1
// and so on...
// If there's an implementation that has no config, you can use a lambda:
class NoConfigImplementation : Instanceable {
companion object {
const val ID = "INSTANCE_2"
}
}
allowedInstances[NoConfigImplementation.ID] = { _ -> NoConfigImplementation() }

How to obtain extension properties by Kotlin reflection?

I have used the memberExtensionProperties() method, but result collection of the extension properties is empty. The test code is attached. What is the right procedure?
class ExtensionPropertyTest {
class DummyClass{}
val DummyClass.id get() = 99
val DummyClass.name get() = "Joe"
#Test
fun testExtensionProperties() {
val dummyClass = DummyClass()
expect(dummyClass.id).toEqual(99) // OK
val properties = DummyClass::class.memberExtensionProperties
.stream()
.toList()
expect(properties).toHaveSize(2) // Fails due a zero size
}
}
memberExtensionProperties does not return extensions over a class, but its members that are at the same time extensions:
fun main() {
println(DummyClass::class.memberExtensionProperties)
}
class DummyClass {
val String.foo: Int
get() = toInt()
}
It is not that easy if at all possible to find all extensions over a class, because extensions are detached from their receivers and they can be located anywhere in the classpath.

How to get value in kotlin?

I remember that in kotlin language there is a option to get value by get() property, but can't find how to write it.
What I mean is: I have a LiveData into my ViewModel and I need that access to post in LiveData has only ViewModel and outside just option to get for subscribe.
How I implemented it for now is
class MyViewModel(ctx: Context) : AndroidViewModel(ctx as Application)
{
private val _showLoadingPB = SingleLiveEvent<Boolean>()
fun showLoadingPB(): SingleLiveEvent<Boolean>
{
return _showLoadingPB
}
...
}
But I remember that there is an option to write it like this
class MyViewModel(ctx: Context) : AndroidViewModel(ctx as Application)
{
private val _showLoadingPB = SingleLiveEvent<Boolean>()
val showLoadingPB: SingleLiveEvent<Boolean>
get() => _showLoadingPB
}
How to make it works?
I remembered how it should be
class MyViewModel(ctx: Context) : AndroidViewModel(ctx as Application)
{
private val _showLoadingPB = SingleLiveEvent<Boolean>()
val showLoadingPB: LiveData<Boolean>
get() = _showLoadingPB
}
This way user can't assign new value to your SingleLiveEvent as well as post new event in LiveData, he can just observe it.

Searching a workaround for kotlin empty data class primary constructor

With given kotlin code :
sealed class Event(val id:String= UUID.randomUUID().toString(), val timestamp:Instant = Instant.now())
data class BarEvent(val additionalInfo:String):Event()
object FooEvent:Event()
// data class CorrectFooEvent():Event() // invalid kotlin
fun main(args: Array<String>) {
val b1 = BarEvent("b1")
val f1 = FooEvent
Thread.sleep(1000)
val b2 = BarEvent("b2")
val f2 = FooEvent
println("${b1.id} ${b1.timestamp} $b1")
println("${f1.id} ${f1.timestamp} $f1")
println("${b2.id} ${b2.timestamp} $b2")
println("${f2.id} ${f2.timestamp} $f2")
}
There is no issue with BarEvent.
But as FooEvent has no more parameter than the ones in Event, I would like it to have empty constructor. It's not authorized for data class, so I made it an object. But object is singleton, so it doesn't behave as an instanciated event.
The only workaround that I see (keeping the class as a data class) is something like :
sealed class Event(open val id:String= UUID.randomUUID().toString(), open val timestamp:Instant = Instant.now())
data class FooEvent(override val id:String= UUID.randomUUID().toString(), override val timestamp:Instant = Instant.now()):Event()
But it's not very elegant.
Just change FooEvent to a normal class, and add (or generate them using your IDE) toString(), hashCode() and equals(Object) if needed:
class FooEvent: Event() {
override hashCode() = ...
override equals(other: Object) {
...
}
override toString() = ...
}
To make the event a data class, simply add an unused property to it. Not pretty, but as short as it can be in Kotlin at the moment:
data class FooEvent(val dummy: Unit = Unit) : Event()
There seems to be no intention to remove this limitation soon:
Data class without arguments deprecated in 1.0. Why?
Suggestion for parameterless data class

Property getter typed on Supertype instead of Implementation in Kotlin

Suppose I have two classes, a Baseand a Implwhich extends Base.
package mypackage
open class Base
class Impl : Base()
How would I create a private property for the concrete Impl-Type (for internal use), with a public getter typed as the Base-Type, to achieve polymorphism? My initial approach was like this:
class Test {
private val myType = Impl()
get():Base
}
However, the Kotlin compiler complains:
Error:(30, 11) Kotlin: Getter return type must be equal to the type of the property, i.e. 'mypackage.Impl'
Basically, this is what it would look like in plain Java:
public class Test {
private Impl myImpl = new Impl();
public Base getBase() {
return myImpl;
}
}
How could one achieve this? Am I missing something?
P.S. I am aware of Backing Fields and creating custom methods as a workaround for getter, I was just curious on how to approach this in an elegant, Kotlin style manner.
If the property is private, so will be the getter. In this case, it doesn't matter what type it will have. If you want to have a public property of base type, you'll need to declare it separately:
private val _myType = Impl()
public val myType : Base
get() = _myType
You would code this the same as you did in Java, using two different properties. Unless you are ok with Impl never being specialized in the class. So here are many options:
// if you don't need Impl typed as Impl then just hold it as base
class Test1 {
public val base: Base = Impl()
}
// have both with pointing one reference at the other
class Test2 {
private val _impl = Impl()
public val base: Base = _impl
}
// have both, second one is a getter (no real benefit over Test2)
class Test3 {
private val _impl = Impl()
public val base: Base
get() = _impl
}
// use a function to do basically a cast
class Test4 {
private val _impl = Impl()
public fun asBase(): Base = _impl
}
Or don't worry about this other property, any use of grabbing the Impl can hold it as type Base:
class Test5 {
public val impl: Impl = Impl()
}
// later
val thing: Base = Test5().impl
Maybe you are looking to build this in a way with a common interface to get the base implementation?
open class Base {}
// a common way to get the implementation from within a class
interface Based {
val base: Base
}
class ImplAbc : Base()
class ImplXyz : Base()
class TestAbc : Based {
override val base: Base = ImplAbc()
}
class TestXyz : Based {
private val _impl = ImplXyz()
override val base: Base = _impl
}