How to make method return type of generated class in KotlinPoet? - kotlinpoet

I need to generate a Builder class with the help of KotlinPoet. For this purpose, I need to make the method return the Builder type. I do it in the following way:
private fun generateInitUserBehaviorClass() = TypeSpec.classBuilder("Init")
.addType(generateInitBuilderClass())
.build()
private fun generateInitBuilderClass() = TypeSpec.classBuilder("Builder")
.addProperty(generateInitBuilderEndpointProperty())
.addFunction(generateInitBuilderEndpointSetter())
.build()
private fun generateInitBuilderEndpointProperty() = PropertySpec.builder(
"endpoint",
Class.forName("android.net.Uri").asTypeName().copy(nullable = true),
KModifier.PRIVATE
).mutable(true)
.initializer("null")
.build()
private fun generateInitBuilderEndpointSetter() = FunSpec.builder("setEndpoint")
.addParameter("endpoint", Class.forName("android.net.Uri"))
.returns(Class.forName("com.idfinance.userbehavior.utils.Init.Builder"))
.build()
But when I build the module I catch the error that Class.forName("com.idfinance.userbehavior.utils.Init.Builder") cannot find class Builder. The package is correct and as I understand the problem is that I try to use class as return type when it is not generated yet. But how can I solve this problem?

You can just instantiate a ClassName using its constructor, it does not have to be a declared class:
private fun generateInitBuilderEndpointSetter() = FunSpec.builder("setEndpoint")
.addParameter("endpoint", ClassName("android.net", "Uri"))
.returns(ClassName("com.idfinance.userbehavior.utils", "Init", "Builder"))
.build()

Related

Problem with creating secondary constructor kotlin

Can someone explain me why I cannot create empty secondary construcor in my class?
I wanna TEST it but I need to create a instance of class to use the methods from, but my class need a parametr to create it. I thought to create a scecondary constructor but when I'm trying it makes a error "There's a cycle in the delegation calls chain". Excatly I wanna use it on this #TEST below but when I'm trying to create instance of Adapter class I must put there also (FragmentManager) inside. Any ideas?
class Adapter(sFM: FragmentManager) : FragmentPagerAdapter(sFM, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
constructor() : this()
private val pFragmentList = ArrayList<Fragment>()
private val pFragmentTitle = ArrayList<String>()
override fun getCount(): Int = pFragmentList.size
override fun getItem(position: Int): Fragment = pFragmentList[position]
override fun getPageTitle(position: Int): CharSequence = pFragmentTitle[position]
fun addFragment(fm: Fragment, title: String) {
pFragmentList.add(fm)
pFragmentTitle.add(title)
}
}
#Test
fun `create instance of class Adapter`() {
var adapter = Adapter().addFragment()
}
There is no FragmentPagerAdapter with empty constructor. Basically, what your code is trying to compile, is to do constructor that calls itself. If you want to use base class constructor you need to use super instead of this. But still, you won't find such constructor in base class. You always have to pass some FragmentManager

MockK: mocking an jpa repository.save call

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

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)

Extension property inside class: Unresolved reference: errorResponse

In my android project:
// Extension Properties
val Response<*>.errorResponse: ErrorResponse
get() = ErrorUtils.parseError(this)
class TransportService {
companion object {
private val SERVICE_UNAVAILABLE_CODE = 503
private val traderMonitorRestClient = RestClientFactory.createRestClient(TraderMonitorRestClient::class.java)
private val serviceUnavailableErrorResponse: Response<*>
}
and here use extension property
class TradersViewModel(application: Application) : AndroidViewModel(application) {
val errorResponse = response.errorResponse
}
nice it's work,
But if I move errorResponse inside class:
class TransportService {
companion object {
private val SERVICE_UNAVAILABLE_CODE = 503
private val traderMonitorRestClient = RestClientFactory.createRestClient(TraderMonitorRestClient::class.java)
private val serviceUnavailableErrorResponse: Response<*>
private val TAG = TransportService::class.java.name
val Response<*>.errorResponse: ErrorResponse
get() = ErrorUtils.parseError(this)
}
}
then I get compile error in use place:
val errorResponse = response.errorResponse
error:
Unresolved reference: errorResponse
This is because the errorResponse extension is only available in the context of the companion object of TransportService when you define it there.
If your extension function doesn't need the companion's properties or methods, there is no real reason to put the extension there. Don't be afraid of polluting the global scope. Technically, you're only defining this extension on your Response object.
That being said, if you really need the companion object's context for your extension function (e.g. if the body of your extension property uses some methods or properties of the companion), you can manually provide the context on the usage site this way:
val errorResponse = with(TransportService.Companion) { response.errorResponse }
This is not limited to companion objects, you just need to provide an instance of the class that contains the extension to with(instance).

How do I test generators for delegated properties?

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
}