0

We have a wrapper class for checking the Android SDK version, and it uses the ChecksSdkIntAtLeast annotation. Here's a trimmed example of the class with just one getter and the logging removed:

class DeviceApi(
    private val deviceOsVersion: Int
) {
    @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.M)
    val isApi23AndAbove get() = deviceOsVersion >= Build.VERSION_CODES.M
}

We have tests for the getter itself, but I was hoping to write tests to verify that the getter also has the annotation in place with the correct value, to avoid the compiler warnings that show up if it isn't included. However, no matter what I try, I can't seem to find the annotation via reflection.

Here's two basic examples that work if I do this for a Kotlin annotation class annotation that just don't find the public @interface annotations provided by androidx.annotation:

@Test
fun kotlin_reflection_example() {
    assertThat(DeviceApi::isApi23AndAbove.getter.hasAnnotation<ChecksSdkIntAtLeast>()).isTrue
}

@Test
fun java_reflection_example() {
    assertThat(DeviceApi::isApi23AndAbove.javaGetter!!.getAnnotation(ChecksSdkIntAtLeast::class.java)).isNotNull
}

I've also tried various examples I've seen online for looking for members and methods from the class and debugged to inspect the state of various things, but I've been unable to get any of the code to see the annotation. I want to know if I'm missing something obvious or if I found a scenario that just isn't supported by kotlin-reflect - and if so, if there's an alternative.

StormFoo
  • 1,129
  • 2
  • 10
  • 25
  • 2
    That annotation is not retained at runtime. That’s why you can’t find it. How about using your own annotation? – Sweeper Jul 28 '23 at 10:32
  • @Sweeper ah, that would explain it, thanks. I don't think writing my own would add anything in this particular scenario, unless I can also get the compiler to treat it the same was as the original does - otherwise it's just extra fluff for a test with no real functionality. If you know of a way to make it testable and functionally similar I'd love to hear more though. – StormFoo Jul 28 '23 at 10:49
  • `hasAnnotation` and `getAnnotation` should really just throw an exception if the annotation doesn't have the correct retention type. Would save so much trouble. – Jorn Jul 28 '23 at 10:57
  • After the comment by @Sweeper I've been able to retain the functionality and add visibility by creating a copy of the in my project with `package androidx.annotation` at the top to override the original and `@Retention(AnnotationRetention.RUNTIME)` on the class to ensure tests can see it. This feels extremely hacky to me though, so I'm holding off on answering my own question with it as a solution in the hope that someone can think of a neater way of doing it. – StormFoo Jul 28 '23 at 11:16
  • 1
    You can process your own annotation by generating the built-in annotation using an annotation processor. See [here](https://proandroiddev.com/the-guide-to-your-first-annotation-processor-with-ksp-and-becoming-a-kotlin-artist-4e5d13f171e6) for a start. – Sweeper Jul 28 '23 at 11:19
  • Turns out ProGuard didn't like the duplicate class and package combo, so the hacky solution wasn't viable in the end. – StormFoo Jul 28 '23 at 16:03

0 Answers0