parametrize @Interface annotation in Java

Viewed 70

I have an @interface annotation type like this -

public @interface someAnnotation {
    String someVar() default "var_value";
}

I want to parametrize someVar on a Junit5 test similar to this

@ParametrizedTest
@ValueSource(strings = {"val1","val2"})
@someAnnotation(someVar = ??)
void theTestMethod(String s){
    //test something
}

I want the value of s (that is provided by valueSource) to be loaded to someVar. Obviously something like this won't work -

@someAnnotation(someVar = s)

I have also tried using Test Extension by implementing BeforeEachCallback to get ExtensionContext using which someVar can be accessed but I have found no way to get ValueSource values or method parameters values from ExtensionContext. Is there any way to achieve this?

1 Answers

The Java Language Specification section on annotations says, in brief, that element values must not be null and may only be:

  • constant expressions
  • class literals
  • enum constants
  • arrays whose elements are one of the above

Therefore, what you're asking for isn't permitted by the language.

The only way I know of to achieve this kind of variability over an annotation's values in a test would be to have the test generate source code and compile it on the fly (or, equivalently, generate bytecode directly).

Stylistically, I would argue that unless your @ValueSource has more than ~50 elements, it's simpler and clearer to the reader to just write out each case like:

@Test
@someAnnotation(someVar = "val1")
void theTestMethodVal1(){
    theTestMethodHelper("val1");
}

@Test
@someAnnotation(someVar = "val2")
void theTestMethodVal2(){
    theTestMethodHelper("val2");
}

void theTestMethodHelper(String s){
    //test something
}

All that having been said, it's not clear to me what the goal of tests like this might be.

As is clear from the hard-coding of "val1" and "val2" in the bodies of the above methods, it's much clearer and more concise to simply inline a needed value in the body of a method, rather than jumping through the reflection hoops that would be necessary to read that same value dynamically from an annotation on the method.

Related