Skip to content

Instantly share code, notes, and snippets.

@translatenix
Created February 26, 2024 01:05
Show Gist options
  • Save translatenix/a1adedb3318f8be1b44fbb30547ef7b3 to your computer and use it in GitHub Desktop.
Save translatenix/a1adedb3318f8be1b44fbb30547ef7b3 to your computer and use it in GitHub Desktop.
Access Kotlin companion objects through JUnit and Java reflection
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
@Target(AnnotationTarget.FIELD)
annotation class KotlinFieldAnnotation
@Target(AnnotationTarget.PROPERTY)
annotation class KotlinPropertyAnnotation
@Target(AnnotationTarget.FUNCTION)
annotation class KotlinFunctionAnnotation
class KotlinTestClass {
companion object {
@KotlinFieldAnnotation
@KotlinPropertyAnnotation
@TempDir // Java field annotation
val companionProperty = false
@KotlinFunctionAnnotation
@BeforeAll // Java method annotation
fun companionMethod() = companionProperty
}
@Test
fun myTest() {}
}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import org.junit.platform.commons.support.HierarchyTraversalMode
import org.junit.platform.commons.support.ModifierSupport
import org.junit.platform.commons.support.ReflectionSupport
// https://kotlinlang.org/docs/annotations.html#annotation-use-site-targets
// https://kotlinlang.org/docs/annotations.html#java-annotations
class ReflectionTest {
@Test
fun `reflect on companion property`() {
val testClassFields = ReflectionSupport.findFields(
KotlinTestClass::class.java,
{ true },
HierarchyTraversalMode.TOP_DOWN)
val companionProperty = testClassFields.find {
it.name == "companionProperty"
}
assertThat(companionProperty).isNotNull
companionProperty!!
assertThat(ModifierSupport.isPrivate(companionProperty)).isTrue()
assertThat(ModifierSupport.isStatic(companionProperty)).isTrue()
// because `val` instead of `var`
assertThat(ModifierSupport.isFinal(companionProperty)).isTrue()
assertThat(companionProperty.isSynthetic).isFalse()
assertThat(companionProperty.isAnnotationPresent(KotlinFieldAnnotation::class.java)).isTrue()
assertThat(companionProperty.isAnnotationPresent(KotlinPropertyAnnotation::class.java)).isFalse()
assertThat(companionProperty.isAnnotationPresent(TempDir::class.java)).isTrue()
assertThat(companionProperty.annotations).hasSize(2)
}
@Test
fun `reflect on companion property with Java reflection`() {
val methods = KotlinTestClass::class.java.declaredMethods
val companionPropertyGetter = methods.find { it.name.startsWith("access$") }
assertThat(companionPropertyGetter).isNotNull
companionPropertyGetter!!
assertThat(ModifierSupport.isPublic(companionPropertyGetter)).isTrue()
assertThat(ModifierSupport.isStatic(companionPropertyGetter)).isTrue()
assertThat(ModifierSupport.isFinal(companionPropertyGetter)).isTrue()
// that's why this method not visible through ReflectionSupport
assertThat(companionPropertyGetter.isSynthetic).isTrue()
assertThat(companionPropertyGetter.isAnnotationPresent(KotlinFieldAnnotation::class.java)).isFalse()
assertThat(companionPropertyGetter.isAnnotationPresent(KotlinPropertyAnnotation::class.java)).isFalse()
assertThat(companionPropertyGetter.isAnnotationPresent(TempDir::class.java)).isFalse()
assertThat(companionPropertyGetter.declaredAnnotations).isEmpty()
}
@Test
fun `reflect on companion method`() {
val companionClass = ReflectionSupport
.findNestedClasses(KotlinTestClass::class.java) {
it.simpleName == "Companion"
}.single()
val companionMethod = ReflectionSupport
.findMethod(companionClass, "companionMethod")
assertThat(companionMethod).isPresent
assertThat(ModifierSupport.isPublic(companionMethod.get())).isTrue()
assertThat(ModifierSupport.isNotStatic(companionMethod.get())).isTrue()
assertThat(ModifierSupport.isFinal(companionMethod.get())).isTrue()
assertThat(companionMethod.get().isSynthetic).isFalse()
assertThat(companionMethod.get().isAnnotationPresent(BeforeAll::class.java)).isTrue()
assertThat(companionMethod.get().isAnnotationPresent(KotlinFunctionAnnotation::class.java)).isTrue()
assertThat(companionMethod.get().annotations).hasSize(2)
}
@Test
fun `reflect on companion object`() {
val companionObjectField = ReflectionSupport.findFields(
KotlinTestClass::class.java,
{ it.name == "Companion" },
HierarchyTraversalMode.TOP_DOWN
).single()
assertThat(ModifierSupport.isPublic(companionObjectField)).isTrue()
assertThat(ModifierSupport.isStatic(companionObjectField)).isTrue()
assertThat(ModifierSupport.isFinal(companionObjectField)).isTrue()
assertThat(companionObjectField.isSynthetic).isFalse()
val companionObject = ReflectionSupport.tryToReadFieldValue(companionObjectField, null)
assertThat(companionObject.toOptional()).isPresent
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment