As you've discovered, you need to add @Retention(RUNTIME)
to your composed annotation in order for JUnit to see it. Annotations in Java have three different retention policies:
RetentionPolicy.SOURCE
Annotations are to be discarded by the compiler.
RetentionPolicy.CLASS
Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time. This is the default behavior. [emphasis added]
RetentionPolicy.RUNTIME
Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
As I emphasized above, if you don't explicitly add @Retention(...)
then the CLASS
policy is used. This won't work with JUnit because JUnit doesn't scan the *.class
files (i.e. byte-code) for the annotations, it scans the loaded classes reflectively to find test methods. Without a RUNTIME
retention policy your annotation is not reflectively accessible, thus JUnit never sees it and consequently doesn't execute the test.
The @Target
annotation:
Indicates the contexts in which an annotation type is applicable. The declaration contexts and type contexts in which an annotation type may be applicable are specified in JLS 9.6.4.1, and denoted in source code by enum constants of java.lang.annotation.ElementType
.
If an @Target
meta-annotation is not present on an annotation type T
, then an annotation of type T
may be written as a modifier for any declaration except a type parameter declaration.
If an @Target
meta-annotation is present, the compiler will enforce the usage restrictions indicated by ElementType
enum constants, in line with JLS 9.7.4.
In my answer to your other question I used:
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
Because that's the same targets used by @ParameterizedTest
. I figured it was a good idea to restrict it to METHOD
since the designers of @ParameterizedTest
apparently feel that only methods should be directly extended by the parameterized-tests extension (see §5 Extension Model). And including ANNOTATION_TYPE
allows you to place your composed annotation on another annotation, creating yet another composed annotation.
You'll also see I included @Documented
:
If the annotation @Documented
is present on the declaration of an annotation type A
, then any @A
annotation on an element is considered part of the element's public contract. In more detail, when an annotation type A
is annotated with Documented
, the presence and value of annotations of type A
are a part of the public contract of the elements A
annotates. Conversely, if an annotation type B
is not annotated with Documented
, the presence and value of B
annotations are not part of the public contract of the elements B
annotates. Concretely, if an annotation type is annotated with Documented
, by default a tool like javadoc will display annotations of that type in its output while annotations of annotation types without Documented
will not be displayed.
Notice that theses annotations—@Retention
, @Target
, and @Documented
—are not specific to JUnit. These annotations are fundamental to how annotations in Java work and each one resides in the java.lang.annotation
package.