Search code examples
javakotlinannotationsmockitomockito-kotlin

Kotlin/Mockito: How to mock "annotationClass" from kotlin.Annotation


I would like to mock annotations to check the good behavior of a class that returns a result according to the given annotation.

Here is one of my tests that check the good behavior for with the OneToOne annotation:

@Test
fun <T> supports_when_field_has_OneToOne_annotation_with_eager_fetch_type() {
    val myHandler = MyHandler<T>()

    val joinAnnotation = mock<OneToOne> {
        on { this.fetch }.thenReturn(FetchType.EAGER)
        onGeneric { this.annotationClass }.thenReturn(OneToOne::class)
    }
    val supports = myHandler.supports(joinAnnotation)

    assertThat(supports).isTrue()
}

When I run my test, I have the following error:

org.mockito.exceptions.misusing.WrongTypeOfReturnValue: KClassImpl cannot be returned by annotationType() annotationType() should return Class


If you're unsure why you're getting above error read on. Due to the nature of the syntax above problem might occur because:

  1. This exception might occur in wrongly written multi-threaded tests. Please refer to Mockito FAQ on limitations of concurrency testing.
  2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
    • with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.

This error occurs when mockito calls the following code:

onGeneric { this.annotationClass }.thenReturn(OneToOne::class)

If I remove this line, I have no problem to mock the Annotation. (mocking 'fetch' attribute works perfectly), but my test doesn't pass because I need to mock 'annotationClass'

I don't understand why I have an error, and why the error is related on annotationType() (java annotation method) ?

Does anyone have an idea how to solve this?


Solution

  • I found a solution to fix my problem.

    kotlin.Annotation.annotationClass is kotlin extension function:

    /**
     * Returns a [KClass] instance corresponding to the annotation type of this annotation.
     */
    public val <T : Annotation> T.annotationClass: KClass<out T>
        get() = (this as java.lang.annotation.Annotation).annotationType().kotlin as KClass<out T>
    

    This function calls java.lang.annotation.Annotation.annotationType()

    I can't mock a extension function because kotlin converts extension functions into static methods and Mockito cannot stub static methods.

    So I mocked directly java.lang.annotation.Annotation.annotationType().

    Here my new code:

    @Test
    fun <T> supports_when_field_has_OneToOne_annotation_with_eager_fetch_type() {
        val myHandler = MyHandler<T>()
    
        val joinAnnotation = mock<OneToOne> {
            on { this.fetch }.thenReturn(FetchType.EAGER)
            on { (this as java.lang.annotation.Annotation).annotationType() }.thenReturn(OneToOne::class.java)
        }
        val supports = myHandler.supports(joinAnnotation)
    
        assertThat(supports).isTrue()
    }
    

    There are alternatives to mock static methods (PowerMock, MockK), but I didn't tested.

    If anyone has a better suggestion, I would be interested in seeing.