JUnit5-Jupiter: Composed (=“meta”) annotation does not resolve to annotation definition

感情迁移 提交于 2020-03-04 20:01:06

问题


I defined my own JUnit annotation:

@ParameterizedTest
@MethodSource("myorg.qa.ccrtesting.DataProviders#standardDataProvider")
@Tags({@Tag("ccr"), @Tag("standard")})
public @interface CcrStandardTest {
}

Then, I was able to use that annotation in my tests:

@CcrStandardTest
public void E0010_contact_standard (String testData) {
...
  • My run configuration:
    JVM options: -ea
    Class: myorg.qa.ccrtesting.ccrstandardtests.CcrStanConTest - This was suggested by the IDE (and is verified to point to the correct class, which holds my prototype test method)

However, this results in: jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [java.lang.String arg0] in method [public void...

  • I tried removing String testData from the test method signature but then JUnit is not executing any tests: No tests found

  • When I add @Test above my prototype test method, it executes but:

    1. It seems like none of the annotations I defined under @CcrStandardTest are applied
    2. IDE suggests suspicious combination @Test and parameterized source
      (I already know @ParameterizedTest implies @Test, just not sure why IDE is able to find the custom annotation but JUnit isn't?)

回答1:


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.




回答2:


Upon some investigation, I found that by adding: @Retention(RUNIME) (two imports will be required), meta annotation definition will be resolved.

JUnit docs actually show this (@Retention) in their example of composed annotations.
They also show @Target being used with it as well.
- But explain neither...

This answer isn't the highest quality, as I don't know what @Retention and @Target do but I hope it'll help anyone stuck with the same problem as me to get going.

If someone elaborates, I will be happy to edit this answer or accept theirs!



来源:https://stackoverflow.com/questions/60339277/junit5-jupiter-composed-meta-annotation-does-not-resolve-to-annotation-def

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!