Applying @DataPoint
or @DataPoints
to a property in a Kotlin class results in "DataPoint field samples must be public" error.
Theories in JUnit4 use @DataPoint
and @DataPoints
annotations to mark sample data which is collected and passed to individual tests that can take them as arguments. When these annotations are applied to Kotlin properties (which are also necessarily annotated @JvmStatic and declared in companion objects), the test cases fail with the error "DataPoint field samples must be public". Kotlin properties are supposed to be public by default. Moreover, explicitly adding a "public" modifier has no affect.
import org.junit.runner.RunWith
import org.junit.experimental.theories.Theories
import org.junit.experimental.theories.DataPoints
import org.junit.experimental.theories.Theory
import org.junit.Assume.*
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.*
@RunWith(Theories::class)
class SampleTheories {
companion object {
@JvmStatic
@DataPoints
public val samples = listOf(
-8, -1, 0, 1, 2, 4, 8
)
}
@Theory
fun triangleInequality(a:Int, b:Int) {
assumeThat(a, `is`(greaterThan(0)))
assumeThat(b, `is`(greaterThan(0)))
assertThat(a+b, `is`(greaterThan(a)))
assertThat(a+b, `is`(greaterThan(b)))
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.51"
// ...
testImplementation 'junit:junit:4.12'
testImplementation 'org.hamcrest:hamcrest-library:1.3'
// ...
}
Under the hood, public properties in Kotlin have a private backing field and public accessors. On the Java side (which is where JUnit4 operates), Kotlin properties do not exist as such. Instead, the fields and accessor methods must be used explicitly.
The error arises because the annotation is applied to the (private) field, rather than the (public) accessor.
There are a couple resolutions:
Apply the @DataPoints
annotation to the property's getter:
@JvmStatic
val samples = listOf(
-8, -1, 0, 1, 2, 4, 8
) @DataPoints get
This will only evaluate the expression once.
Declare the data points as a method, rather than a property:
@JvmStatic
@DataPoints
fun samples = listOf(
-8, -1, 0, 1, 2, 4, 8
)
Since the data point source is a function, each time they're retrieved (which may not be many times; I'm not familiar with the internals of Theories), the expression will be re-evaluated.