Verifying method call with enum in Kotlin - kotlin

I'm trying to verify that a method is called with a given argument. That argument is a non-nullable enum type. So I get the exception eq(SomeEnum.foo) must not be null. Here is a sample what I'm trying to do:
enum class SomeEnum {
foo, bar
}
open class MyClass {
fun doSomething() {
magic(SomeEnum.foo)
}
internal fun magic(whatever: SomeEnum) {}
}
#Test
fun mockitoBug() {
val sut = spy(MyClass())
sut.doSomething()
verify(sut).magic(eq(SomeEnum.foo))
}
Capturing does not work too. What can I do or is that really a bug as I assume?

Because Mockito was designed for Java, it doesn't play well with Kotlin's null checks. A good solution is to use the mockito-kotlin extensions library: https://github.com/mockito/mockito-kotlin
It includes Kotlin versions of the matchers that won't return null. Add a dependency on mockito-kotlin and just make sure to import the Kotlin versions instead of the Java ones.

Related

How to avoid NPEs from default parameters in mocked classes?

Here's a simplified version of what I want to test with Mockito:
class UnderTest {
fun doSomething() {
foo.doAnything()
}
}
class Foo {
fun doAnything(bar: Bar = Bar())
}
class TestUnderTest {
#Mock
var underTest: UnderTest
#Test
fun testDoSomething() {
underTest.doSomething() // Causes NPE
}
}
UnderTest is being tested. Its dependencies, like foo, are mocked. However, when my tests call UnderTest.doSomething(), it crashes. doSomething() calls Foo.doAnything(), letting it fill in the null parameter with the default - and the code that runs in that default parameter initialization is outside of the control of my test, as it's inside the static, synthetic method created for the byte code.
Is there a magical Mockito solution to get around this very situation? If so, I would love to hear it. Otherwise, I believe the options I have are:
To use PowerMock or Mockk to be able to mock things Mockito can't
To change Foo to have two doAnything() methods; one would have zero parameters, would call Bar() and pass it to the other.
To change Foo.doAnything() to accept a nullable parameter, then to have the body of the function call Bar() and use it.

How to create compile-time constant in Kotlin from enum?

I have an annotation that requires defaultValue to be compile-time constant. I take defaultValue from enum below:
enum class RaceType {
MARATHON,
SPRINT;
companion object {
fun apply(type: RaceType): RaceDto {
return when (type) {
MARATHON -> MarathonDto()
SPRINT -> SprintDto()
}
}
}
}
My dtos are the following:
interface RaceDto {
}
data class MarathonDto: RaceDto
data class SprintDto: RaceDto
when I use annotation #QraphQLArgument(defaultValue = RaceType.SPRINT.name) Kotlin requires RaceType.SPRINT.name to be compile-time constant.
Annotation implementation itself:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.PARAMETER})
public #interface GraphQLArgument {
String NONE = "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
String NULL = "\n\t\t\n\t\t\n\ue000\ue001\ue002\ue003\n\t\t\t\t\n";
String name();
String description() default "";
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
Class<? extends DefaultValueProvider> defaultValueProvider() default JsonDefaultValueProvider.class;
}
I looked through similar questions but don't see a way how it can be resolved. I also found article related to the topic but nothing worked so far.
Side note: I cannot change annotation since it is from the library and I cannot change the library as well.
To summarize, is there a way to make from enum compile-time constant in Kotlin to use in an annotation?
is there a way to make from enum compile-time constant in Kotlin to use in an annotation?
No, because formally enums aren't compile-time constants in Java.
However please consider the sealed classes:
sealed class RaceType {
object MARATHON: RaceType() {
const val name = "MARATHON" // copy-paste is required here until https://youtrack.jetbrains.com/issue/KT-16304
}
object SPRINT: RaceType()
companion object {
fun apply(type: RaceType): RaceDto {
return when (type) { // the check is in compile time, because of sealed class
MARATHON -> MarathonDto()
SPRINT -> SprintDto()
}
}
}
}
A little part of copy-paste is still required. Please vote on kotlin compiler bug or follow this thread.
However, as I understand, this doesn't solve your issue with #QraphQLArgument(defaultValue = RaceType.SPRINT.name) unfortunately, because the name of class is not the same with value. In the other words, with sealed classes you need to write code to convert input strings to them.

Kotlin multiplatform support for Optional

I'm working with a Java API now converted into multiplatform Kotlin. It used to use java.lang.Optional as the return type of many calls. I understand this is not the idiomatic Kotlin-way (see discussion) but this is an existing API, Optional stays (also it isn't a bad choice for the Java-facing client). My question is how?
Note: The code only needs to return Optional.of(x) or return Optional.empty() to the external API. Any internal uses will be purged.
How do we use expect/actual/typealias to use the real Optional class when available?
Is there a way to avoid re-implementing a fake Optional class on non-Java targets (i.e. work idiomatically with nullable? suffix)
At this point, Kotlin doesn't allow providing an actual typealias for an expected class with a companion object by using a Java class with matching static declarations. Follow this issue for updates: KT-29882.
For now, you can workaround that by declaring the factory functions separately, outside the expected Optional class, as follows:
expect class Optional<T : Any> {
fun get(): T
fun isPresent(): Boolean
/* ... */
}
expect object Optionals {
fun <T : Any> of(t: T): Optional<T>
fun empty(): Optional<Nothing>
}
That should not necessarily be an object, you could just use top-level functions.
Then, on the JVM, you would have to provide an actual typealias for the Optional class and, additionally, provide the trivial actual implementation for the Optionals object:
actual typealias Optional<T> = java.util.Optional<T>
actual object Optionals {
actual fun <T : Any> of(t: T): Optional<T> = java.util.Optional.of(t)
actual fun empty(): Optional<Nothing> = java.util.Optional.empty()
}
As for not providing an implementation for the non-JVM platforms, I doubt it's possible, as that would require some non-trivial compile-time transformations of the Optional usages to just the nullable type. So you would want something like this:
actual typealias Optional<T> = T?
which is now an error:
Type alias expands to T?, which is not a class, an interface, or an object
So you actually need a non-JVM implementation. To avoid duplicating it for every non-JVM target, you can declare a custom source set and link it with the platform-specific source sets, so they get the implementation from there:
build.gradle.kts
kotlin {
/* targets declarations omitted */
sourceSets {
/* ... */
val nonJvmOptional by creating {
dependsOn(getByName("commonMain"))
}
configure(listOf(js(), linuxX64())) { // these are my two non-JVM targets
compilations["main"].defaultSourceSet.dependsOn(nonJvmOptional)
}
}
}
Then, inside this custom source set (e.g. in src/nonJvmOptional/kotlin/OptionalImpl.kt) you can provide an actual implementation for the non-JVM targets.
Here's a minimal project example on Github where I experimented with the above: h0tk3y/mpp-optional-demo

Converting Kotlin's KClass to regular Class in Java

I am trying call a regular Java method in a Java code as follows:
public <T> T proxy(KClass<T> kClass) {
// unfortunately nothing like getJavaClass() exists
return (T) proxy(kClass.getJavaClass());
}
public <T> T proxy(Class<T> jClass) {
return (T) context.getBean(jClass);
}
In Kotlin, you can call .java on each KClass. This is not the case here and I am unable to extract the Java Class object from KClass. Is there a way to do it?
EDIT: This is trivial in Kotlin, but I am looking for solution in Java code.
The functionality does exist, just not where it seems to, as java is an extension property.
Use the method JvmClassMappingKt.getJavaClass.
In Kotlin, extension methods (and property getters/setters) are implemented as static methods of their containing class. If you look at the source for .java (Ctrl+Q), you can see that it is implemented in JvmClassMapping.kt.
As the function is package-level and does not have a containing object, it is simply placed into the file [Filename]Kt which in this case is JvmClassMappingKt.
Here is the source of this extension property:
#Suppress("UPPER_BOUND_VIOLATED")
public val <T> KClass<T>.java: Class<T>
#JvmName("getJavaClass")
get() = (this as ClassBasedDeclarationContainer).jClass as Class<T>
As you can see, the method's name is renamed on the JVM to getJavaClass.
In your case, you can try:
public <T> T proxy(KClass<T> kClass) {
return (T) proxy(JvmClassMappingKt.getJavaClass(kClass));
}
You can try to use javaObjectType on your KClass
The explanation:
Returns a Java [Class] instance corresponding to the given [KClass] instance.
In case of primitive types it returns corresponding wrapper classes.
E.g.
Boolean::class.javaObjectType

Strange "Val cannot be reassigned" error when setting a property in Kotlin of a Java object

The strange thing is that my Kotlin code compiled fine before, when it looked like this in the java class Allocator:
public void setAllocMethod(#NotNull AllocMethod allocMethod) {
this.allocMethod = allocMethod;
}
but when I changed the java class' setter to this:
public void setAllocMethod(#Nullable AllocMethod allocMethod) {
this.allocMethod= allocMethod;
}
then when I compile the project, I get this Kotlin error in the kt file that calls the java object:
Val Cannot be Reassigned
allocator.allocMethod = DefaultAllocMethod() // kotlin code
also here is the java getter:
public #NotNull AllocMethod getAllocMethod() {
if (allocMethod == null) allocMethod = DefaultAllocMethod.newDefault();
return allocMethod;
}
DefaultAllocMethod is a java subclass of AllocMethod
allocator is of type Allocator, which is a java class that has the getter and setter described above.
Can anyone explain what is happening? thanks
Your setters's type #Nullable AllocMethod, which is Kotlin's AllocMethod?, does not match the getters type #NotNull AllocMethod, which is Kotlin's AllocMethod
What the error message means is that since the types do not match, only the getter is considered as a property. So from the Kotlin's point of view instead of a var allocMethod you have val allocMethod and fun setAllocMethod(...)
Remember that an AllocMethod? is an Any? and that an AllocMethod is an Any. That helps to understand why these getters and setters don't match up.